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

com.unister.semweb.drums.bucket.Bucket Maven / Gradle / Ivy

The newest version!
/* Copyright (C) 2012-2013 Unister GmbH
 * 
 * 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; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * 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.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
package com.unister.semweb.drums.bucket;

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

import com.unister.semweb.drums.DRUMSParameterSet;
import com.unister.semweb.drums.api.DRUMS;
import com.unister.semweb.drums.bucket.hashfunction.AbstractHashFunction;
import com.unister.semweb.drums.file.FileLockException;
import com.unister.semweb.drums.storable.AbstractKVStorable;
import com.unister.semweb.drums.storable.GeneralStorable;
import com.unister.semweb.drums.util.AbstractKVStorableComparator;
import com.unister.semweb.drums.util.KeyUtils;

/**
 * An instance of this class is a container of {@link AbstractKVStorable}s.
 * 
 * @author Martin Nettling, Nils Thieme
 * @param 
 *            an implementation of {@link AbstractKVStorable}, e.g. {@link GeneralStorable}
 */
public class Bucket {

    /**
     * contains all memory chunks and its elements to store. We use byte-arrays to save memory, because mostly the whole
     * object-structure takes double memory for small objects.
     */
    private byte[][] memory;

    private int lastChunkIndex = -1;
    private int position_in_chunk = 0;
    private long memorySizeInBytes = 0;

    /** the id of the bucket. Should be known by its {@link BucketContainer} and its {@link AbstractHashFunction} */
    private final int bucketId;

    /** the number of elements in this bucket. For faster access, should be public */
    public int elementsInBucket;

    /** prototype of type Data (extending {@link AbstractKVStorable}) for instantiating correct arrays */
    private Data prototype;

    /** Stores the creation time of this bucket. */
    private long creationTime;

    /** A pointer to the {@link DRUMSParameterSet} used by this {@link DRUMS} */
    private DRUMSParameterSet gp;

    /**
     * Constructor. Needs to know the id of the {@link Bucket} and the maximum size of the {@link Bucket}.
     * 
     * @param bucketId
     *            the identification number of this bucket. Used in {@link BucketContainer} and other classes
     * @param gp
     *            a pointer to the {@link DRUMSParameterSet}
     */
    public Bucket(final int bucketId, DRUMSParameterSet gp) {
        this.bucketId = bucketId;
        this.memory = new byte[0][];
        this.elementsInBucket = 0;
        this.prototype = gp.getPrototype();
        this.creationTime = System.currentTimeMillis();
        this.gp = gp;
    }

    /**
     * returns a new empty Bucket with the same properties of this bucket
     * 
     * @return a new {@link Bucket} with the same {@link #bucketId}
     * 
     * @throws IOException
     * @throws FileLockException
     */
    public Bucket getEmptyBucketWithSameProperties() throws FileLockException, IOException {
        Bucket newBucket = new Bucket(this.bucketId, gp);
        return newBucket;
    }

    /**
     * @return the id of the bucket. The id is value between 0 and numberOfBuckets (see {@link BucketContainer})
     */
    public int getBucketId() {
        return bucketId;
    }

    /**
     * Adds one {@link AbstractKVStorable}-object. This method have to be synchronized, because it is possible to access
     * the backend in the same moment with the function {@link #getBackend()}.
     * 
     * @param toAdd
     *            the Data to add
     * @return true, if adding the element to the buffer was successful
     */
    public synchronized boolean add(AbstractKVStorable toAdd) {
        if (memorySizeInBytes >= gp.MAX_MEMORY_PER_BUCKET) {
            return false;
        }
        // no memory is available
        try {
            if (lastChunkIndex == -1 || position_in_chunk == memory[lastChunkIndex].length) {
                boolean allocatingSuccessful = enlargeMemory();

                // No more memory was left so we don't add the element and return false.
                if (!allocatingSuccessful) {
                    return false;
                }
            }
        } catch (InterruptedException e) {
            // TODO: error log
            e.printStackTrace();
        }

        byte[] b = toAdd.toByteBuffer().array();
        for (int i = 0; i < b.length; i++, position_in_chunk++) {
            memory[lastChunkIndex][position_in_chunk] = b[i];
        }
        elementsInBucket++;
        return true;
    }

    /**
     * @param element
     *            the element to look for
     * 
     * @return true, if this bucket, contains the given element
     */
    public boolean contains(Data element) {
        boolean contains = false;
        byte[] dst = new byte[gp.getElementSize()];
        for (int m = 0; m < memory.length; m++) {
            ByteBuffer bb = ByteBuffer.wrap(memory[m]);
            if (m == memory.length - 1) {
                bb.limit(position_in_chunk);
            }
            while (bb.remaining() > 0) {
                bb.get(dst);
                if (KeyUtils.compareKey(element.toByteBuffer().array(), dst) > 0) {
                    contains = true;
                    break;
                }
            }
            if (contains) {
                break;
            }
        }
        return contains;
    }

    /**
     * Enlarges the memory by one chunk. If this was successful true will be returned, otherwise
     * false.
     * 
     * @throws InterruptedException
     */
    private boolean enlargeMemory() throws InterruptedException {
        int bytesAllocated = DynamicMemoryAllocater.INSTANCES[gp.instanceID].allocateNextChunk();
        if (bytesAllocated > 0) {

            byte[] mem = new byte[bytesAllocated];
            byte[][] new_mem = new byte[memory.length + 1][];
            for (int i = 0; i < memory.length; i++) {
                new_mem[i] = memory[i];
            }
            lastChunkIndex++;
            new_mem[lastChunkIndex] = mem;
            memory = new_mem;
            position_in_chunk = 0;
            return true;
        } else {
            return false;
        }
    }

    private void sort() {

    }

    /**
     * @return a ByteBuffer containing the requested element. The returned ByteBuffer is in read-only mode.
     * 
     * @param index
     *            the index of the element in the bucket
     * 
     */
    public ByteBuffer getElementAt(int index) {
        if (index >= elementsInBucket) {
            return null;
        } else {
            int chunk = index * gp.getElementSize() / memory[0].length;
            int offset = index * gp.getElementSize() % memory[0].length;
            return ByteBuffer.wrap(memory[chunk], offset, gp.getElementSize()).asReadOnlyBuffer();
        }
    }

    /**
     * Returns the in backend stored {@link AbstractKVStorable}s. First it rebuilds all objects from their
     * byte-arrays and then sorts them.
     * 
     * @return {@link AbstractKVStorable}[] all {@link AbstractKVStorable}s ascending sorted
     */
    // TODO: handle backend internally as long byte-array
    @SuppressWarnings("unchecked")
    public synchronized Data[] getBackend() {
        sort();
        AbstractKVStorable[] data = new AbstractKVStorable[elementsInBucket];
        byte[] dst = new byte[gp.getElementSize()];
        int i = 0;
        for (int m = 0; m < memory.length; m++) {
            ByteBuffer bb = ByteBuffer.wrap(memory[m]);
            if (m == memory.length - 1) {
                bb.limit(position_in_chunk);
            }
            while (bb.remaining() > 0) {
                bb.get(dst);
                data[i++] = prototype.fromByteBuffer(ByteBuffer.wrap(dst));
            }
        }
        Arrays.sort(data, new AbstractKVStorableComparator());
        return (Data[]) data;
    }

    /** @return the number of elements, actually stored */
    public int size() {
        return elementsInBucket;
    }

    /**
     * This method frees the allocated memory.
     * 
     * @return the number of bytes which are available now
     */
    public int freeMemory() {
        int size = 0;
        for (int m = 0; m < memory.length; m++) {
            size += memory[m].length;
            memory[m] = null;
        }
        memory = null;
        return size;
    }

    /** @return the creation time of this bucket. */
    public long getCreationTime() {
        return creationTime;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy