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

com.bigdata.btree.AbstractTuple 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 May 30, 2008
 */

package com.bigdata.btree;

import java.nio.ByteBuffer;
import java.util.Arrays;

import com.bigdata.btree.data.ILeafData;
import com.bigdata.io.ByteArrayBuffer;
import com.bigdata.io.DataInputBuffer;
import com.bigdata.io.DataOutputBuffer;
import com.bigdata.rawstore.IBlock;
import com.bigdata.rawstore.IRawStore;

/**
 * Abstract base class with much of the functionality of {@link ITuple}.
 * 
 * @author Bryan Thompson
 * @version $Id$
 */
public abstract class AbstractTuple implements ITuple {
    
    final protected int flags;
    
    final protected boolean needKeys;
    
    final protected boolean needVals;

    final public int flags() {
        
        return flags;
        
    }
    
    final public boolean getKeysRequested() {
        
        return needKeys;
        
    }
    
    final public boolean getValuesRequested() {
        
        return needVals;
        
    }

    /**
     * Reused for each key that is materialized and null if keys
     * are not being requested. Among other things, the {@link #kbuf} access may
     * be used to completely avoid heap allocations when considering keys. The
     * data for the current key are simply copied from the leaf into the
     * {@link #kbuf} and the application can either examine the data in the
     * {@link #kbuf} or copy it into its own buffers.
     */
    final private DataOutputBuffer kbuf;
    
    final public ByteArrayBuffer getKeyBuffer() {
        
        if (!needKeys)
            throw new UnsupportedOperationException();
        
        return kbuf;
        
    }

    /**
     * Return a stream from which the key may be read.
     * 
     * @throws UnsupportedOperationException
     *             if the keys were not requested.
     */
    final public DataInputBuffer getKeyStream() {

        if (!needKeys)
            throw new UnsupportedOperationException();

        if (keyStream == null) {

            // setup the input stream.

            keyStream = new DataInputBuffer(kbuf.array(), 0/* offset */, kbuf
                    .limit()/* len */);

        } else {

            // reset the buffer.

            keyStream.setBuffer(kbuf.array(), 0, kbuf.limit());

        }

        return keyStream;

    }

    private DataInputBuffer keyStream = null;

    /**
     * Return a stream from which the value may be read.
     * 
     * @throws UnsupportedOperationException
     *             if the values were not requested.
     */
    final public DataInputBuffer getValueStream() {

        if (!needVals)
            throw new UnsupportedOperationException();

        if (isNull)
            throw new UnsupportedOperationException();

        if (versionDeleted)
            throw new UnsupportedOperationException();

        if (valStream == null) {

            // setup the input stream.

            valStream = new DataInputBuffer(vbuf.array(), 0/* offset */, vbuf
                    .limit()/* len */);

        } else {

            // reset the buffer.

            valStream.setBuffer(vbuf.array(), 0, vbuf.limit());

        }
        
        return valStream;
        
    }
    private DataInputBuffer valStream = null;
    
    /**
     * Reused for each value that is materialized and null if
     * values are not being requested.
     */
    final private DataOutputBuffer vbuf;

    /**
     * true iff the iterator is visiting an index entry with a
     * null value.
     */
    private boolean isNull;
    
    private long versionTimestamp = 0L;

    /**
     * Note: The default of false has the correct semantics if
     * the btree does not support isolation since isolation requires that both
     * deleted entries and version timestamps are enabled.
     */
    private boolean versionDeleted = false;
    
    /**
     * The #of entries that have been visited. This will be ZERO (0) until the
     * first entry has been visited, at which point it is incremented to ONE
     * (1), etc.
     */
    private int nvisited = 0;
    
    /**
     * The #of entries that have been visited so far and ZERO (0) until the
     * first entry has been visited.
     */
    public long getVisitCount() {
        
        return nvisited;
        
    }
    
    /** Disallowed. */
    private AbstractTuple() {
        
        throw new UnsupportedOperationException();
        
    }

    public AbstractTuple(final int flags) {
        
        this.flags = flags;
        
        needKeys = (flags & IRangeQuery.KEYS) != 0;
        
        needVals = (flags & IRangeQuery.VALS) != 0;
        
        if(needKeys) {
            
            /*
             * Note: we are choosing a smallish initial capacity. We could
             * specify ZERO (0) to never over estimate, but the buffer will be
             * re-sized automatically. An initial non-zero value makes it more
             * likely that we will not have to re-allocate. The smallish initial
             * value means that we are not allocating or wasting that much space
             * if we in fact require less (or more).
             */

            kbuf = new DataOutputBuffer(128);
            
        } else {
            
            kbuf = null;
            
        }
        
        if(needVals) {

            vbuf = new DataOutputBuffer(128);
            
        } else {
            
            vbuf = null;
            
        }
        
    }

    final public byte[] getKey() {

        if (!needKeys)
            throw new UnsupportedOperationException();

        return kbuf.toByteArray();

    }

    final public boolean isNull() {
    
        return isNull;
        
    }
    
    final public byte[] getValue() {

        if (!needVals)
            throw new UnsupportedOperationException();

        if (versionDeleted || isNull)
            return null;

        return vbuf.toByteArray();

    }

    final public ByteArrayBuffer getValueBuffer() {

        if(versionDeleted)
            throw new UnsupportedOperationException();

        if (isNull)
            throw new UnsupportedOperationException();
        
        return vbuf;
        
    }

    @SuppressWarnings("unchecked")
    final public E getObject() {

        if (versionDeleted) {

            // Visiting a deleted entry.
            return null;
            
        }

        return (E) getTupleSerializer().deserialize(this);
        
    }

    final public long getVersionTimestamp() {

        return versionTimestamp;
        
    }

    final public boolean isDeletedVersion() {

        return versionDeleted;
        
    }

    /**
     * @todo Implement. In order to work the tuple needs to know the source
     *       store from which the index entry was read. In the context of a
     *       fused view this is the element of that view that supplied the tuple
     *       binding (blocks may be stored on journals or index segments). In
     *       the context of a scale-out index, the state must include enough
     *       information to locate the data service for the index view and then
     *       the source index within that view.
     *       

* Consider the notion of leases for the read back from a data * service. If you do not read on the block soon enough the lease is * gone and you will get an error if you try to read on the block. The * lease can be used to limit the time that an index partition is * locked to a data service. */ public IBlock readBlock(long addr) { throw new UnsupportedOperationException(); } /** * Copy data and metadata for the index entry from the {@link Leaf} into the * {@link Tuple} and increment the counter of the #of visited entries. * * @param index * The index entry. * @param leaf * The leaf. If a raw record must be materialized, it will be * read from the backing store for the {@link Leaf}. * * @todo The various copy methods should also set the [sourceIndex] property * and {@link ITuple#getSourceIndex()} should be implemented by this * class (or maybe add a setSourceIndex() to be more flexible). */ public void copy(final int index, final ILeafData leaf) { nvisited++; /* * true iff delete markers are enabled and this entry is marked deleted. */ versionDeleted = leaf.hasDeleteMarkers() && leaf.getDeleteMarker(index); /* * the version timestamp iff timestamps are enabled and otherwise 0L. */ versionTimestamp = leaf.hasVersionTimestamps() ? leaf .getVersionTimestamp(index) : 0L; if (needKeys) { kbuf.reset(); leaf.getKeys().copy(index, kbuf); } if (needVals) { vbuf.reset(); if (!versionDeleted) { isNull = leaf.getValues().isNull(index); if (!isNull) { if (leaf.hasRawRecords()) { final long addr = leaf.getRawRecord(index); if (addr == IRawStore.NULL) { // copy out of the leaf. leaf.getValues().copy(index, vbuf); } else { // materialize from the backing store. final ByteBuffer tmp = ((IRawRecordAccess) leaf) .readRawRecord(addr); // and copy into the value buffer. vbuf.copy(tmp); } } else { // copy out of the leaf. leaf.getValues().copy(index, vbuf); } } } } } /** * Assumes that the tuple is not deleted and that timestamp information is * not available. * * @param key * @param val */ public void copyTuple(final byte[] key, final byte[] val) { copyTuple(key, val, false/* deleted */, 0L/* timestamp */); } /** * Sets all fields on the tuple and increments the tuple visited counter. * * @param key * @param val * @param deleted * @param timestamp */ public void copyTuple(final byte[] key, final byte[] val, final boolean deleted, final long timestamp) { this.nvisited++; this.versionDeleted = deleted; this.versionTimestamp = timestamp; if (needKeys) { kbuf.reset(); kbuf.put(key); } if (needVals) { vbuf.reset(); if (!versionDeleted) { isNull = val == null; if (!isNull) { vbuf.put(val); } } } } /** * Sets all fields and increments the tuple visited counter. * * @param t * Some tuple. */ public void copyTuple(ITuple t) { this.nvisited++; this.versionDeleted = t.isDeletedVersion(); this.versionTimestamp = t.getVersionTimestamp(); if (needKeys) { kbuf.reset().copyAll(t.getKeyBuffer()); // final ByteArrayBuffer tmp = t.getKeyBuffer(); // kbuf.put(tmp.array(), 0/* offset */, tmp.limit()); } if (needVals) { vbuf.reset(); if (!versionDeleted) { isNull = t.isNull(); if (!isNull) { vbuf.reset().copyAll(t.getValueBuffer()); // final ByteArrayBuffer tmp = t.getValueBuffer(); // // vbuf.put(tmp.array(), 0/* offset */, tmp.limit()); } } } } /** * Clears the buffered data copied into the {@link AbstractTuple} from the * source btree. */ protected void clear() { this.versionDeleted = false; this.versionTimestamp = 0L; if (kbuf != null) this.kbuf.reset(); if (vbuf != null) this.vbuf.reset(); this.isNull = true; } /** * Change the tuple state to reflect the fact that the tuple has been * deleted from the backing index. */ void markDeleted() { this.versionDeleted = true; this.versionTimestamp = 0L; if (vbuf != null) this.vbuf.reset(); this.isNull = true; } /** * Note: A de-serialization problem thrown out of {@link #toString()} MAY * indicate that the {@link ITupleSerializer} was not overriden and a raw * byte[] was stored as the tuple's value. In this case, * {@link #getObject()} will attempt to use Java standard de-serialization * on the byte[] value, which will, of course, fail. The fix for this * problem is to specify the {@link NOPTupleSerializer} for the index. This * problem may still show up in some of the test suites when the trace level * is turned up since many of the tests were written before the * {@link ITupleSerializer} abstraction was addeded. */ public String toString() { return toString(this); } /** * Utility method returns a {@link String} representation of an * {@link ITuple}. * * @param tuple * The {@link ITuple}. * * @return The {@link String} representation of that {@link ITuple}. */ public static String toString(final ITuple tuple) { Object obj; try { obj = tuple.getObject(); } catch(UnsupportedOperationException ex) { // note: indicates Not Available rather than [null]. obj = "N/A"; } catch(Throwable t) { /* * Note: The most common error here is an attempt to de-serialize a * byte[] that does not in fact contain a serialized object. This * typically happens when the DefaultTupleSerializer was not * overridden with a NOPTupleSerializer and the application (or often * the test case) is storing byte[]s rather than Objects. */ obj = t.getMessage(); } final StringBuilder sb = new StringBuilder(); sb.append(tuple.getClass().getName() + "@" + tuple.hashCode()); sb.append("{ nvisited=" + tuple.getVisitCount()); sb.append(", flags=" + flagString(tuple.flags())); if (tuple.isDeletedVersion()) { sb.append(", deleted"); } if (tuple.getVersionTimestamp() != 0L) { sb.append(", timestamp=" + tuple.getVersionTimestamp()); } sb.append(", key=" + (tuple.getKeysRequested() ? Arrays.toString(tuple.getKey()) : "N/A")); sb.append(", val=" + (tuple.getValuesRequested() ? (tuple.isNull() ? "null" : Arrays.toString(tuple.getValue())) : "N/A")); sb.append(", obj=" + (obj instanceof byte[] ? Arrays .toString((byte[]) obj) : obj)); sb.append(", sourceIndex=" + tuple.getSourceIndex()); sb.append("}"); return sb.toString(); } /** * Externalizes the flags as a list of symbolic constants. * * @param flags * The {@link IRangeQuery} flags. * * @return The list of symbolic constants. */ public static String flagString(final int flags) { final StringBuilder sb = new StringBuilder(); // #of flags that are turned on. int onCount = 0; sb.append("["); if ((flags & IRangeQuery.KEYS) != 0) { if (onCount++ > 0) sb.append(","); sb.append("KEYS"); } if ((flags & IRangeQuery.VALS) != 0) { if (onCount++ > 0) sb.append(","); sb.append("VALS"); } if ((flags & IRangeQuery.DELETED) != 0) { if (onCount++ > 0) sb.append(","); sb.append("DELETED"); } if ((flags & IRangeQuery.READONLY) != 0) { if (onCount++ > 0) sb.append(","); sb.append("READONLY"); } if ((flags & IRangeQuery.REMOVEALL) != 0) { if (onCount++ > 0) sb.append(","); sb.append("REMOVEALL"); } if ((flags & IRangeQuery.CURSOR) != 0) { if (onCount++ > 0) sb.append(","); sb.append("CURSOR"); } if ((flags & IRangeQuery.REVERSE) != 0) { if (onCount++ > 0) sb.append(","); sb.append("REVERSE"); } if ((flags & IRangeQuery.FIXED_LENGTH_SUCCESSOR) != 0) { if (onCount++ > 0) sb.append(","); sb.append("FIXED_LENGTH_SUCCESSOR"); } if ((flags & IRangeQuery.PARALLEL) != 0) { if (onCount++ > 0) sb.append(","); sb.append("PARALLEL"); } sb.append("]"); return sb.toString(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy