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

scala.reactive.container.ReactHashMap.scala Maven / Gradle / Ivy

The newest version!
package scala.reactive
package container



import scala.collection._
import scala.reflect.ClassTag



class ReactHashMap[@spec(Int, Long, Double) K, V >: Null <: AnyRef]
  (implicit val can: ReactHashMap.Can[K, V])
extends ReactMap[K, V] with ReactBuilder[(K, V), ReactHashMap[K, V]] with PairBuilder[K, V, ReactHashMap[K, V]] {
  private var table: Array[ReactHashMap.Entry[K, V]] = null
  private var elemCount = 0
  private var entryCount = 0
  private[reactive] var keysContainer: ReactContainer.Emitter[K] = null
  private[reactive] var valuesContainer: ReactContainer.Emitter[V] = null
  private[reactive] var entriesContainer: PairContainer.Emitter[K, V] = null
  private[reactive] var insertsEmitter: Reactive.Emitter[(K, V)] = null
  private[reactive] var removesEmitter: Reactive.Emitter[(K, V)] = null


  protected def init(k: K) {
    table = new Array(ReactHashMap.initSize)
    keysContainer = new ReactContainer.Emitter[K](f => foreachKey(f), () => size)
    valuesContainer = new ReactContainer.Emitter[V](f => foreachValue(f), () => size)
    entriesContainer = new PairContainer.Emitter[K, V]()
    insertsEmitter = new Reactive.Emitter[(K, V)]
    removesEmitter = new Reactive.Emitter[(K, V)]
  }

  init(null.asInstanceOf[K])

  def keys: ReactContainer[K] = keysContainer
  def values: ReactContainer[V] = valuesContainer
  def inserts: Reactive[(K, V)] = insertsEmitter
  def removes: Reactive[(K, V)] = removesEmitter
  def entries: PairContainer[K, V] = entriesContainer

  def builder: ReactBuilder[(K, V), ReactHashMap[K, V]] = this

  def +=(kv: (K, V)) = {
    insertPair(kv._1, kv._2)
  }

  def -=(kv: (K, V)) = {
    removePair(kv._1, kv._2)
  }

  def insertPair(k: K, v: V) = {
    insert(k, v)
    true
  }

  def removePair(k: K, v: V) = {
    delete(k, v) != null
  }

  def container = this

  val react = new ReactHashMap.Lifted[K, V](this)

  def foreachEntry(f: ReactHashMap.Entry[K, V] => Unit) {
    var i = 0
    while (i < table.length) {
      var entry = table(i)
      while (entry ne null) {
        f(entry)
        entry = entry.next
      }
      i += 1
    }
  }

  def foreachKey(f: K => Unit) = foreachEntry {
    e => if (e.value != null) f(e.key)
  }

  def foreachValue(f: V => Unit) = foreachEntry {
    e => if (e.value != null) f(e.value)
  }

  def foreachPair(f: (K, V) => Unit) = foreachEntry {
    e => if (e.value != null) f(e.key, e.value)
  }

  def foreach(f: ((K, V)) => Unit) = foreachEntry {
    e => if (e.value != null) f((e.key, e.value))
  }

  private def lookup(k: K): ReactHashMap.Entry[K, V] = {
    val pos = index(k)
    var entry = table(pos)

    while (entry != null && entry.key != k) {
      entry = entry.next
    }

    if (entry == null) null
    else entry
  }

  private[reactive] def ensure(k: K): ReactHashMap.Entry[K, V] = {
    val pos = index(k)
    var entry = table(pos)
    checkResize()

    while (entry != null && entry.key != k) {
      entry = entry.next
    }

    if (entry == null) {
      entry = can.newEntry(k, this)
      entry.next = table(pos)
      table(pos) = entry
      entry
    } else entry
  }

  private[reactive] def clean(entry: ReactHashMap.Entry[K, V]) {
    if (entry.value == null) {
      val pos = index(entry.key)
      table(pos) = table(pos).remove(entry)
    }
  }

  private def emitRemoves(k: K, previousValue: V) {
    keysContainer.removes += k
    valuesContainer.removes += previousValue
    entriesContainer.removes.emit(k, previousValue)
    if (removesEmitter.hasSubscriptions) removesEmitter += (k, previousValue)
  }

  private def insert(k: K, v: V): V = {
    assert(v != null)
    checkResize()

    val pos = index(k)
    var entry = table(pos)

    while (entry != null && entry.key != k) {
      entry = entry.next
    }

    var previousValue: V = null
    if (entry == null) {
      entry = can.newEntry(k, this)
      entry.value = v
      entry.next = table(pos)
      table(pos) = entry
      entryCount += 1
    } else {
      previousValue = entry.value
      entry.value = v
    }

    if (previousValue == null) elemCount += 1
    else emitRemoves(k, previousValue)
    
    {
      keysContainer.inserts += k
      valuesContainer.inserts += v
      entriesContainer.inserts.emit(k, v)
      if (insertsEmitter.hasSubscriptions) insertsEmitter += (k, v)
    }
    
    entry.propagate()

    previousValue
  }

  private def delete(k: K, expectedValue: V = null): V = {
    val pos = index(k)
    var entry = table(pos)

    while (entry != null && entry.key != k) {
      entry = entry.next
    }

    if (entry == null) null
    else {
      val previousValue = entry.value

      if (expectedValue == null || expectedValue == previousValue) {
        entry.value = null

        elemCount -= 1
        if (!entry.hasSubscriptions) {
          table(pos) = table(pos).remove(entry)
          entryCount -= 1
        }

        emitRemoves(k, previousValue)
        entry.propagate()

        previousValue
      } else null
    }
  }

  private def checkResize() {
    if (entryCount * 1000 / ReactHashMap.loadFactor > table.length) {
      val otable = table
      val ncapacity = table.length * 2
      table = new Array(ncapacity)
      elemCount = 0
      entryCount = 0

      var opos = 0
      while (opos < otable.length) {
        var entry = otable(opos)
        while (entry != null) {
          val nextEntry = entry.next
          val pos = index(entry.key)
          entry.next = table(pos)
          table(pos) = entry
          entryCount += 1
          if (entry.value != null) elemCount += 1
          entry = nextEntry
        }
        opos += 1
      }
    }
  }

  private def index(k: K): Int = {
    val hc = k.##
    math.abs(scala.util.hashing.byteswap32(hc)) % table.length
  }

  private def noKeyError(key: K) = throw new NoSuchElementException("key: " + key)

  def applyOrNil(key: K): V = {
    val entry = lookup(key)
    if (entry == null || entry.value == null) null
    else entry.value
  }

  def apply(key: K): V = {
    val entry = lookup(key)
    if (entry == null || entry.value == null) noKeyError(key)
    else entry.value
  }

  def get(key: K): Option[V] = {
    val entry = lookup(key)
    if (entry == null || entry.value == null) None
    else Some(entry.value)
  }

  def contains(key: K): Boolean = {
    val entry = lookup(key)
    if (entry == null || entry.value == null) false
    else true
  }

  def update(key: K, value: V): Unit = {
    insert(key, value)
  }

  def remove(key: K): Boolean = delete(key) match {
    case null => false
    case v => true
  }

  def clear() {
    var pos = 0
    while (pos < table.length) {
      var entry = table(pos)
      while (entry != null) {
        val nextEntry = entry.next
        val previousValue = entry.value

        entry.value = null
        if (!entry.hasSubscriptions) table(pos) = table(pos).remove(entry)
        entry.propagate()
        if (previousValue != null) {
          elemCount -= 1
          emitRemoves(entry.key, previousValue)
        }

        entry = nextEntry
      }

      pos += 1
    }
    if (elemCount != 0) {
      throw new IllegalStateException("Size not zero after clear: " + elemCount)
    }
  }

  def size: Int = elemCount
  
  override def toString = {
    val elemCount = mutable.Buffer[(K, V)]()
    for (kv <- this) elemCount += kv
    s"ReactHashMap($size, ${elemCount.mkString(", ")})"
  }

}


object ReactHashMap {

  trait Entry[@spec(Int, Long, Double) K, V >: Null <: AnyRef]
  extends Signal.Default[V] {
    def outer: ReactHashMap[K, V]
    def key: K
    def value: V
    def value_=(v: V): Unit
    def next: Entry[K, V]
    def next_=(e: Entry[K, V]): Unit
    def apply(): V = value
    def propagate() = reactAll(value)
    def remove(e: Entry[K, V]): Entry[K, V] = if (this eq e) next else {
      if (next ne null) next = next.remove(e)
      this
    }
    override def onSubscriptionChange() = if (!hasSubscriptions) outer.clean(this)
    override def toString = s"Entry($key, $value)"
  }

  trait Can[@spec(Int, Long, Double) K, V >: Null <: AnyRef] {
    def newEntry(key: K, outer: ReactHashMap[K, V]): ReactHashMap.Entry[K, V]
  }

  implicit def canAnyRef[K, V >: Null <: AnyRef] = new Can[K, V] {
    def newEntry(k: K, o: ReactHashMap[K, V]) = new ReactHashMap.Entry[K, V] {
      def outer = o
      def key = k
      var value: V = _
      var next: Entry[K, V] = null
    }
  }

  implicit def canInt[V >: Null <: AnyRef] = new Can[Int, V] {
    def newEntry(k: Int, o: ReactHashMap[Int, V]) = new ReactHashMap.Entry[Int, V] {
      def outer = o
      def key = k
      var value: V = _
      var next: Entry[Int, V] = null
    }
  }

  implicit def canLong[V >: Null <: AnyRef] = new Can[Long, V] {
    def newEntry(k: Long, o: ReactHashMap[Long, V]) = new ReactHashMap.Entry[Long, V] {
      def outer = o
      def key = k
      var value: V = _
      var next: Entry[Long, V] = null
    }
  }

  implicit def canDouble[V >: Null <: AnyRef] = new Can[Double, V] {
    def newEntry(k: Double, o: ReactHashMap[Double, V]) = new ReactHashMap.Entry[Double, V] {
      def outer = o
      def key = k
      var value: V = _
      var next: Entry[Double, V] = null
    }
  }

  def apply[@spec(Int, Long, Double) K, V >: Null <: AnyRef](implicit can: Can[K, V]) = new ReactHashMap[K, V]()(can)

  class Lifted[@spec(Int, Long, Double) K, V >: Null <: AnyRef](val container: ReactHashMap[K, V])
  extends ReactMap.Lifted[K, V] {
    def apply(k: K): Signal[V] = {
      container.ensure(k).signal(container.applyOrNil(k))
    }
  }

  val initSize = 16

  val loadFactor = 750

  implicit def factory[@spec(Int, Long, Double) K, V >: Null <: AnyRef] = new ReactBuilder.Factory[(K, V), ReactHashMap[K, V]] {
    def apply() = ReactHashMap[K, V]
  }

  implicit def pairFactory[@spec(Int, Long, Double) K, V >: Null <: AnyRef] = new PairBuilder.Factory[K, V, ReactHashMap[K, V]] {
    def apply() = ReactHashMap[K, V]
  }

}









© 2015 - 2025 Weber Informatics LLC | Privacy Policy