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

izumi.thirdparty.internal.boopickle.IdentMap.scala Maven / Gradle / Ivy

package izumi.thirdparty.internal.boopickle

/**
  * Specialized fast and cheap to initialize identity map for pickle state identifier map
  */
private[izumi] abstract class IdentMap {
  def apply(obj: AnyRef): Option[Int]

  def updated(obj: AnyRef): IdentMap
}

private[izumi] object IdentMap {
  def empty: IdentMap = EmptyIdentMap
}

private[izumi] object EmptyIdentMap extends IdentMap {
  override def apply(obj: AnyRef): Option[Int] = None

  override def updated(obj: AnyRef): IdentMap = new IdentMap1(obj)
}

private[boopickle] final class IdentMap1(o1: AnyRef) extends IdentMap {
  override def apply(obj: AnyRef): Option[Int] = {
    if (ReferenceEquality.eq(obj, o1))
      Some(2)
    else None
  }

  override def updated(obj: AnyRef): IdentMap = new IdentMap2(o1, obj)
}

private[boopickle] final class IdentMap2(o1: AnyRef, o2: AnyRef) extends IdentMap {
  override def apply(obj: AnyRef): Option[Int] = {
    if (ReferenceEquality.eq(obj, o1))
      Some(2)
    else if (ReferenceEquality.eq(obj, o2))
      Some(3)
    else None
  }

  override def updated(obj: AnyRef): IdentMap = new IdentMap3Plus(o1, o2, obj)
}

private[izumi] object IdentMap3Plus {

  private[boopickle] class Entry(val hash: Int, val obj: AnyRef, val idx: Int, var next: Entry)

}

private[boopickle] final class IdentMap3Plus(o1: AnyRef, o2: AnyRef, o3: AnyRef) extends IdentMap {
  import IdentMap3Plus.Entry

  var hashSize  = 64
  val maxDepth  = 1
  var hashTable = new Array[Entry](hashSize)
  // indices 0 (not used) and 1 (for null) are reserved
  var curIdx = 2

  // initialize with data
  updated(o1)
  updated(o2)
  updated(o3)

  @inline private def hashIdx(hash: Int) = {
    val h = scala.util.hashing.byteswap32(hash)
    ((h >> 16) ^ (h >> 8) ^ h) & (hashSize - 1)
  }

  override def apply(obj: AnyRef): Option[Int] = {
    val hash     = ReferenceEquality.identityHashCode(obj)
    val tableIdx = hashIdx(hash)
    var e        = hashTable(tableIdx)
    while ((e != null) && ReferenceEquality.ne(e.obj, obj)) e = e.next
    if (e == null)
      None
    else
      Some(e.idx)
  }

  override def updated(obj: AnyRef): IdentMap = {
    val hash     = ReferenceEquality.identityHashCode(obj)
    val tableIdx = hashIdx(hash)
    hashTable(tableIdx) = new Entry(hash, obj, curIdx, hashTable(tableIdx))
    curIdx += 1
    // should we switch to next gear?
    if (curIdx > hashSize * maxDepth)
      resize()
    this
  }

  /**
    * Resizes the underlying hash table to make indexing fast as the number of entries grows
    */
  private def resize(): Unit = {
    val newSize  = hashSize * 4
    val newTable = new Array[Entry](newSize)
    // copy old entries
    var i = hashSize - 1
    hashSize = newSize
    while (i >= 0) {
      var e = hashTable(i)
      while (e != null) {
        val tableIdx = hashIdx(e.hash)
        val n        = e.next
        e.next = newTable(tableIdx)
        newTable(tableIdx) = e
        e = n
      }
      i -= 1
    }
    hashTable = newTable
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy