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

io.reactors.common.UnrolledRing.scala Maven / Gradle / Ivy

package io.reactors
package common



import scala.annotation.tailrec
import scala.collection._



class UnrolledRing[@specialized(Byte, Short, Int, Float, Long, Double) T](
  implicit val arrayable: Arrayable[T]
) {
  import UnrolledRing._

  private[reactors] var start: Node[T] = _
  private[reactors] var end: Node[T] = _
  private[reactors] var size: Int = _

  private[reactors] def init(a: Arrayable[T]) {
    start = new Node(arrayable.newRawArray(INITIAL_NODE_LENGTH), 0, 0)
    start.next = start
    end = start
    size = 0
  }

  init(arrayable)

  private[reactors] final def advance() {
    if (start.isEmpty && start.next.nonEmpty && start != end) {
      start.reset()
      start = start.next
    }
  }

  def clear() {
    init(arrayable)
  }

  def nonEmpty: Boolean = {
    if (start.nonEmpty) true
    else {
      advance()
      start.nonEmpty
    }
  }

  def isEmpty: Boolean = !nonEmpty

  def head: T = {
    if (nonEmpty) start.head
    else throw new NoSuchElementException("empty")
  }

  def enqueue(elem: T) {
    end.enqueue(this, elem)
    size += 1
  }

  def dequeue(): T = {
    advance()
    val elem = start.dequeue(this)
    size -= 1
    elem
  }

  def remove(elem: T): Int = {
    val at = UnrolledRing.remove(this, null, start, elem, 0)
    if (at != -1) size -= 1
    at
  }

  def foreach(f: T => Unit) {
    @tailrec def foreach(n: Node[T]) {
      val array = n.array
      var i = n.start
      while (i < n.until) {
        f(array(i))
        i += 1
      }
      if (n != end) foreach(n.next)
    }
    foreach(start)
  }

  private[reactors] def nodes: Seq[Node[T]] = {
    var curr = start
    val buffer = mutable.Buffer[Node[T]]()
    do {
      buffer += curr
      curr = curr.next
    } while (curr != end)
    buffer
  }

  def debugString = {
    var chain = ""
    var n = start
    do {
      var ptr = ""
      if (n == start) ptr += "$"
      if (n == end) ptr += "^"
      chain += s"$ptr[${n.start}, ${n.until}: ${n.array.mkString(", ")}] --> "
      n = n.next
    } while (n != start)
    s"UnrolledRing($chain)"
  }

}


object UnrolledRing {

  val INITIAL_NODE_LENGTH = 8
  val MAXIMUM_NODE_LENGTH = 128

  class Node[@specialized(Byte, Short, Int, Float, Long, Double) T](
    val array: Array[T], var start: Int, var until: Int
  ) {
    var next: Node[T] = null

    final def isEmpty = start == until

    final def nonEmpty = !isEmpty

    def head = array(start)

    def reset() {
      start = 0
      until = 0
    }

    private def reserve(ring: UnrolledRing[T]) {
      val nextlen = math.min(MAXIMUM_NODE_LENGTH, array.length * 4)
      val fresh = new Node[T](ring.arrayable.newRawArray(nextlen), 0, 0)
      fresh.next = this.next
      this.next = fresh
      ring.end = fresh
    }

    def enqueue(ring: UnrolledRing[T], elem: T) {
      if (until < array.length) {
        array(until) = elem
        until += 1
        // TODO(axel22): This prematurely allocates without checking. Fix, and add
        // ScalaMeter tests for boxing.
        if (until == array.length) {
          if (this.isEmpty) {
            this.reset()
          } else if (next.isEmpty && next.start == 0) {
            next.reset()
            ring.end = next
          } else {
            reserve(ring)
          }
        }
      } else if (this.isEmpty) {
        this.reset()
        this.enqueue(ring, elem)
      } else if (next.isEmpty) {
        next.reset()
        next.enqueue(ring, elem)
        ring.end = next
      } else {
        reserve(ring)
        this.next.enqueue(ring, elem)
      }
    }

    def dequeue(ring: UnrolledRing[T]): T = {
      if (isEmpty) throw new NoSuchElementException("empty")

      val elem = array(start)
      array(start) = null.asInstanceOf[T]
      start += 1
      elem
    }
  }

  @tailrec final def remove[@specialized(Byte, Short, Int, Float, Long, Double) T](
    ring: UnrolledRing[T], prev: Node[T], curr: Node[T], elem: T, at: Int
  ): Int = {
    var position = -1

    //println(
    //  s"rem $elem in ${curr.array.mkString(", ")}::: ${curr.start}, ${curr.until}")
    var i = curr.start
    while (i < curr.until) {
      if (curr.array(i) == elem) {
        position = i
        i = curr.until
      } else i += 1
    }

    //println(s"position: $position")
    if (position != -1) {
      var j = position + 1
      while (j < curr.until) {
        curr.array(j - 1) = curr.array(j)
        j += 1
      }
      curr.until -= 1
      curr.array(curr.until) = null.asInstanceOf[T]

      // check if the node is completely empty
      if (curr.start == curr.until) {
        if (curr.next ne curr) {
          // there are at least 2 nodes
          if (prev == null) {
            // remove start node
            ring.end.next = curr.next
            ring.start = curr.next
          } else {
            prev.next = curr.next
            if (curr == ring.end) ring.end = prev
          }
        }
      }

      at + position
    } else if (curr == ring.end) -1
    else remove(ring, curr, curr.next, elem, at + curr.until - curr.start)
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy