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

org.opalj.collection.mutable.RefArrayBuffer.scala Maven / Gradle / Ivy

The newest version!
/* BSD 2-Clause License - see OPAL/LICENSE for details. */
package org.opalj.collection.mutable

import scala.reflect.ClassTag
import java.util.{Arrays ⇒ JArrays}

import org.opalj.collection.RefIterator
import org.opalj.collection.immutable.RefArray

/**
 * An array based implementation of a mutable buffer. This implementation offers highly
 * optimized, but very unsafe methods and is therefore __not__ a general purpose data-structure.
 * In general, this buffer should only be used to reference objects which outlive the life time
 * of the buffer AND where the buffer is only used locally. To foster a local usage only, we do
 * not inherit from any standard collection classes. One use case is for example the usage
 * as the backing data-structure for work list algorithms.
 *
 * @note This data structure is not thread safe.
 *
 * @param data The array containing the values.
 * @param size0 The number of stored values.
 * @author Michael Eichberg
 */
final class RefArrayBuffer[N >: Null <: AnyRef] private (
        private var data:  Array[N],
        private var size0: Int
) { buffer ⇒

    /**
     * Resets the size of the buffer, but does not clear the underlying array; hence,
     * the array may prevent the garbage collection of the still referenced values.
     * This is generally not a problem if the array is only used locally and the
     * referenced (dead) objects outlive the lifetime of the buffer!
     */
    def _UNSAFE_resetSize(): Unit = size0 = 0

    def size: Int = size0
    def length: Int = size0
    def isEmpty: Boolean = size0 == 0
    def nonEmpty: Boolean = size0 > 0

    def ensureAdditionalCapacity(capacity: Int): Unit = {
        assert(capacity >= 1)
        if (size0 + capacity >= data.length) {
            data = JArrays.copyOf[N](data, (data.length + capacity) * 3 / 2)
        }
    }

    def apply(index: Int): N = {
        if (index < 0 || index >= size0)
            throw new IndexOutOfBoundsException(s"$index (size: $size0)");

        data(index).asInstanceOf[N]
    }

    def toArray[T >: N: ClassTag]: Array[T] = {
        val target = new Array[T](size0)
        System.arraycopy(data, 0, target, 0, size0)
        target
    }

    /**
     * Returns a reference to the underlying mutable array if it is (by chance) completely
     * full; otherwise a new array which just contains the valid entries is returned.
     */
    // IMPROVE Design an annotation|analysis that ensures that the buffer is no longer used afterwards!
    def _UNSAFE_toArray: Array[N] = {
        if (size0 == data.length)
            data
        else
            JArrays.copyOf[N](data, size0)
    }

    def toSet[T >: N <: AnyRef]: Set[T] = {
        val b = Set.newBuilder[T]
        this.foreach(e ⇒ b += e)
        b.result()
    }

    /**
     * Extracts the slice of the given size.
     *
     * @param from the index of the first item (inclusive)
     * @param until the index of the last item (exclusive)
     */
    def slice(from: Int, until: Int = size0): RefArray[N] = { // TODO Use RefArray
        if (until > size0)
            throw new IndexOutOfBoundsException(s"$until > $size0(size of buffer)")

        RefArray._UNSAFE_from[N](JArrays.copyOfRange(data, from, until, classOf[Array[AnyRef]]))
    }

    def ++=(is: Traversable[N]): this.type = {
        is.foreach(+=)
        this
    }

    def ++=(is: Iterator[N]): this.type = {
        is.foreach(+=)
        this
    }

    def ++=(other: RefArrayBuffer[N]): this.type = {
        if (data.length - size0 >= other.data.length) {
            System.arraycopy(other.data, 0, this.data, size0, other.data.length)
        } else {
            val newData = java.util.Arrays.copyOf(data, this.size0 + other.size0 + 8)
            System.arraycopy(other.data, 0, newData, this.size0, other.size0)
            data = newData
            this.data = newData
        }
        this.size0 = this.size0 + other.size0
        this
    }

    /**
     * Copies all values from the given array to this buffer in one step.
     */
    def ++=(other: Array[N]): this.type = {
        if (data.length - size0 >= other.length) {
            System.arraycopy(other, 0, this.data, size0, other.length)
        } else {
            val newData = java.util.Arrays.copyOf(data, this.size0 + other.length + 8)
            System.arraycopy(other, 0, newData, this.size0, other.length)
            data = newData
            this.data = newData
        }
        this.size0 = this.size0 + other.length
        this
    }

    def +=(i: N): this.type = {
        val size0 = this.size0
        var data = this.data
        if (data.length == size0) {
            val newData = java.util.Arrays.copyOf(data, (size0 + 1) * 2)
            data = newData
            this.data = newData
        }

        data(size0) = i
        this.size0 = size0 + 1
        this
    }

    def head: N = {
        if (this.size0 == 0)
            throw new NoSuchElementException("the buffer is empty");

        this.data(0)
    }

    def last: N = {
        if (this.size0 == 0)
            throw new NoSuchElementException("the buffer is empty");

        this.data(size0 - 1)
    }

    def foreach[U](f: N ⇒ U): Unit = {
        val data = this.data
        val size = this.size0
        var i = 0
        while (i < size) {
            f(data(i))
            i += 1
        }
    }

    /**
     * Returns an iterator which iterates over the values starting with the value
     * at the given `startIndex`.
     * '''The iterator will not check for updates of the underlying collection.'''
     *
     * @param startIndex index of the first element that will be returned (inclusive)
     */
    def iteratorFrom(startIndex: Int): RefIterator[N] = new RefIterator[N] {
        private[this] var index = startIndex
        override def hasNext: Boolean = index < size0
        override def next(): N = {
            val r = data(index)
            index += 1
            r.asInstanceOf[N]
        }
    }

    /**
     * Returns an iterator which iterates over the values in the specified range.
     * '''The iterator will not check for updates of the underlying collection.'''
     *
     * @note    The `next` method will throw an `IndexOutOfBoundsException`
     *          when all elements are already returned.
     *
     * @param from index of the first element that will be returned (inclusive)
     * @param until index of the last element (exclusive)
     */
    def iterator(from: Int = 0, until: Int = buffer.size0): RefIterator[N] = {
        val lastIndex = Math.min(until, buffer.size0)
        new RefIterator[N] {
            private[this] var index = from
            def hasNext: Boolean = index < lastIndex
            def next(): N = {
                val currentIndex = this.index
                val r = buffer.data(currentIndex)
                this.index = currentIndex + 1
                r.asInstanceOf[N]
            }
        }
    }

    override def toString: String = {
        s"RefArrayBuffer(size=$size0; data=${data.take(size0).mkString("[", ",", "]")})"
    }
}

object RefArrayBuffer {

    def empty[N >: Null <: AnyRef: ClassTag]: RefArrayBuffer[N] = withInitialSize[N](4)

    def withInitialSize[N >: Null <: AnyRef: ClassTag](initialSize: Int): RefArrayBuffer[N] = {

        new RefArrayBuffer[N](new Array[N](Math.max(initialSize, 1)), 0)
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy