de.sciss.lucre.data.HASkipList.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of lucre-data_sjs1_2.13 Show documentation
Show all versions of lucre-data_sjs1_2.13 Show documentation
Extension of Scala-STM, adding optional durability layer, and providing API for confluent and reactive event layers
/*
* HASkipList.scala
* (Lucre 4)
*
* Copyright (c) 2009-2022 Hanns Holger Rutz. All rights reserved.
*
* This software is published under the GNU Affero General Public License v3+
*
*
* For further information, please contact Hanns Holger Rutz at
* [email protected]
*/
package de.sciss.lucre
package data
import de.sciss.lucre.impl.MutableImpl
import de.sciss.serial.{DataInput, DataOutput, TFormat, WritableFormat}
import scala.annotation.{switch, tailrec}
import scala.collection.immutable.{IndexedSeq => Vec, Set => ISet}
import scala.collection.mutable
/** A transactional version of the deterministic k-(2k+1) top-down operated skip list
* as described in T. Papadakis, Skip Lists and Probabilistic Analysis of
* Algorithms. Ch. 4 (Deterministic Skip Lists), pp. 55--78. Waterloo (CA) 1993
*
* It uses the horizontal array technique with a parameter for k (minimum gap size).
* It uses a modified top-down removal algorithm that avoids the need for a second
* pass as in the original algorithm, and is careful about object creations, so that
* it will be able to persist the data structure without any unnecessary reads or
* writes to the store.
*
* Three implementation notes: (1) We treat the nodes as immutable at the moment, storing them
* directly in the S#Val child pointers of their parents. While this currently seems to
* have a performance advantage (?), we could try to avoid this by using S#Refs for
* the child pointers, making the nodes become mutable. We could avoid copying the
* arrays for each insertion or deletion, at the cost of more space, but maybe better
* performance.
*
* (2) The special treatment of `isRight` kind of sucks. Since now that information is
* also persisted, we might just have two types of branches and leaves, and avoid passing
* around this flag.
*
* (3) Since there is a bug with the top-down one-pass removal, we might end up removing
* the creation of instances of virtual branches altogether again when replacing the
* current algorithm by a two-pass one.
*
* TODO: nodes or at least leaves should be horizontally connected for a faster iterator
* and fast pair (interval) search
*/
object HASkipList {
private def opNotSupported: Nothing = sys.error("Operation not supported")
private final val SER_VERSION = 76
private final class SetFmt[T <: Exec[T], A](keyObserver: SkipList.KeyObserver[T, A])
(implicit ordering: TOrdering[T, A],
keyFormat: TFormat[T, A])
extends TFormat[T, HASkipList.Set[T, A]] {
override def readT(in: DataInput)(implicit tx: T): HASkipList.Set[T, A] =
HASkipList.Set.read[T, A](in, keyObserver)
override def write(list: HASkipList.Set[T, A], out: DataOutput): Unit = list.write(out)
override def toString = "HASkipList.Set.format"
}
private final class MapFmt[T <: Exec[T], A, B](keyObserver: SkipList.KeyObserver[T, A])
(implicit ordering: TOrdering[T, A],
keyFormat: TFormat[T, A],
valueFormat: TFormat[T, B])
extends WritableFormat[T, HASkipList.Map[T, A, B]] {
override def readT(in: DataInput)(implicit tx: T): HASkipList.Map[T, A, B] =
HASkipList.Map.read[T, A, B](in, keyObserver)
override def toString = "HASkipList.Map.format"
}
def debugFindLevel[T <: Exec[T], A, E](list: SkipList[T, A, E], key: A)(implicit tx: T): Int =
list match {
case impl0: HASkipList[T, A, E] =>
val h = impl0.height
@tailrec
def stepRight(impl: HASkipList[T, A, E], n: Node[T, A, E], lvl: Int): Int = {
val idx = impl.indexInNodeR(key, n)
if (idx < 0) lvl
else if (n.isLeaf) -1
else {
val c = n.asBranch.down(idx)
if (idx < n.size - 1) stepLeft(impl, c, lvl - 1) else stepRight(impl, c, lvl - 1)
}
}
@tailrec
def stepLeft(impl: HASkipList[T, A, E], n: Node[T, A, E], lvl: Int): Int = {
val idx = impl.indexInNodeL(key, n)
if (idx < 0) lvl else if (n.isLeaf) -1 else stepLeft(impl, n.asBranch.down(idx), lvl - 1)
}
val c = impl0.top.orNull // topN
if (c eq null) -1 else stepRight(impl0, c, h)
case _ => sys.error(s"Not a HA Skip List: $list")
}
private final class SetImpl[T <: Exec[T], A](id: Ident[T], val minGap: Int,
protected val keyObserver: SkipList.KeyObserver[T, A],
_downNode: SetImpl[T, A] => Var[T, Node[T, A, A]])
(implicit val ordering: TOrdering[T, A],
val keyFormat: TFormat[T, A])
extends Impl[T, A, A](id) with HASkipList.Set[T, A] {
protected val downNode: Var[T, Node[T, A, A]] = _downNode(this)
override def toString = s"SkipList.Set$id"
def add (key: A)(implicit tx: T): Boolean = addEntry(key, key).isEmpty
def remove(key: A)(implicit tx: T): Boolean = removeEntry(key).isDefined
def firstKey(implicit tx: T): A = head
def lastKey (implicit tx: T): A = last
def +=(key: A)(implicit tx: T): this.type = {
addEntry(key, key)
this
}
protected def newLeaf(key: A): Leaf[T, A, A] = {
val lKeys = Vector[A](key, null.asInstanceOf[A])
new SetLeaf[T, A](lKeys)
}
def writeEntry(key: A, out: DataOutput): Unit = keyFormat.write(key, out)
override protected def readLeaf(in: DataInput, isRight: Boolean)(implicit tx: T): Leaf[T, A, A] = {
val sz = in.readByte().toInt
val szi = if (isRight) sz - 1 else sz
val keys = Vector.tabulate[A](sz) { i =>
if (i < szi) keyFormat.readT(in) else null.asInstanceOf[A]
}
new SetLeaf[T, A](keys)
}
}
private final class MapImpl[T <: Exec[T], A, B](id: Ident[T], val minGap: Int,
protected val keyObserver: SkipList.KeyObserver[T, A],
_downNode: MapImpl[T, A, B] => Var[T, Map.Node[T, A, B]])
(implicit val ordering : TOrdering[T, A],
val keyFormat : TFormat[T, A],
val valueFormat : TFormat[T, B])
extends Impl[T, A, (A, B)](id) with HASkipList.Map[T, A, B] {
protected val downNode: Var[T, Map.Node[T, A, B]] = _downNode(this)
override def toString = s"SkipList.Map$id"
def put(key: A, value: B)(implicit tx: T): Option[B] = addEntry(key, (key, value)).map(_._2)
def remove(key: A)(implicit tx: T): Option[B] = removeEntry(key).map(_._2)
def firstKey(implicit tx: T): A = head._1
def lastKey (implicit tx: T): A = last._1
def +=(entry: (A, B))(implicit tx: T): this.type = {
addEntry(entry._1, entry)
this
}
def writeEntry(entry: (A, B), out: DataOutput): Unit = {
keyFormat .write(entry._1, out)
valueFormat.write(entry._2, out)
}
protected def newLeaf(entry: (A, B)): Leaf[T, A, (A, B)] = {
val en = Vector(entry, null.asInstanceOf[(A, B)])
new MapLeaf[T, A, B](en)
}
def keysIterator(implicit tx: T): Iterator[A] = {
val i = new KeyIteratorImpl
i.init()
i
}
def valuesIterator(implicit tx: T): Iterator[B] = {
val i = new ValueIteratorImpl
i.init()
i
}
def get(key: A)(implicit tx: T): Option[B] = {
@tailrec
def stepRight(n: Node[T, A, (A, B)]): Option[B] = {
val idx = indexInNodeR(key, n)
val idxP = if (idx < 0) -(idx + 1) else idx
if (n.isLeaf) {
if (idx < 0)
Some(n.asLeaf.entry(idxP)._2)
else
None
} else {
val c = n.asBranch.down(idxP)
if (idxP < n.size - 1) stepLeft(c) else stepRight(c)
}
}
@tailrec
def stepLeft(n: Node[T, A, (A, B)]): Option[B] = {
val idx = indexInNodeL(key, n)
val idxP = if (idx < 0) -(idx + 1) else idx
if (n.isLeaf) {
if (idx < 0)
Some(n.asLeaf.entry(idxP)._2)
else
None
} else {
stepLeft(n.asBranch.down(idxP))
}
}
val c = topN
if (c eq null) None else stepRight(c)
}
def getOrElse[B1 >: B](key: A, default: => B1)(implicit tx: T): B1 =
get(key).getOrElse(default) // XXX TODO --- could optimize this at some point
def getOrElseUpdate(key: A, op: => B)(implicit tx: T): B =
get(key).getOrElse { // XXX TODO --- could optimize this at some point
val value = op
put(key, value)
value
}
private final class KeyIteratorImpl(implicit tx: T) extends IteratorImpl[A] {
protected def getValue(l: Leaf[T, A, (A, B)], idx: Int): A = l.key(idx)
override def toString = "KeyIterator"
}
private final class ValueIteratorImpl(implicit tx: T) extends IteratorImpl[B] {
protected def getValue(l: Leaf[T, A, (A, B)], idx: Int): B = l.entry(idx)._2
override def toString = "ValueIterator"
}
override protected def readLeaf(in: DataInput, isRight: Boolean)(implicit tx: T): Leaf[T, A, (A, B)] = {
val sz = in.readByte().toInt
val szi = if (isRight) sz - 1 else sz
val en = Vector.tabulate(sz) { i =>
if (i < szi) {
val key = keyFormat .readT(in)
val value = valueFormat.readT(in)
(key, value)
} else {
null.asInstanceOf[(A, B)]
}
}
new MapLeaf[T, A, B](en)
}
}
private abstract class Impl[T <: Exec[T], A, E](final val id: Ident[T])
extends HASkipList[T, A, E] with HeadOrBranch[T, A, E] with TFormat[T, Node[T, A, E]] with MutableImpl[T] {
impl =>
// ---- abstract ----
protected def downNode: Var[T, Node[T, A, E]]
protected def keyObserver: SkipList.KeyObserver[T, A]
def writeEntry(entry: E, out: DataOutput): Unit
protected def newLeaf(entry: E): Leaf[T, A, E]
protected def readLeaf(in: DataInput, isRight: Boolean)(implicit tx: T): Leaf[T, A, E]
// ---- impl ----
// implicit private[this] def head: Impl[T, A, E] = this
final def arrMinSz: Int = minGap + 1
private[this] def arrMaxSz: Int = (minGap + 1) << 1 // aka arrMinSz << 1
private[this] val hasObserver = keyObserver != SkipList.NoKeyObserver
protected final def writeData(out: DataOutput): Unit = {
out.writeByte(SER_VERSION)
out.writeByte(minGap)
downNode.write(out)
}
final def clear()(implicit tx: T): Unit = {
// we just replace `downNode`. for confluent,
// the updates wouldn't make any difference,
// anyway. for durable, perhaps we have GC some day...
// def step(n: Node[T, A, E]): Unit = {
// if (n.isBranch) {
// val b = n.asBranch
// val bsz = b.size
// var i = 0; while (i < bsz) {
// step(b.down(i))
// b.downRef(i).dispose()
// i += 1
// }
// }
// }
//
// val c = topN
// if (c ne null) {
// step(c)
downNode() = null
// }
}
protected final def disposeData()(implicit tx: T): Unit =
downNode.dispose()
def size(implicit tx: T): Int = {
val c = topN
if (c eq null) 0 else c.leafSizeSum - 1
}
final def maxGap: Int = (minGap << 1) + 1 // aka arrMaxSz - 1
final def isEmpty (implicit tx: T): Boolean = topN eq null
final def nonEmpty(implicit tx: T): Boolean = !isEmpty
final def height(implicit tx: T): Int = {
var n = topN
if (n eq null) 0
else {
var h = 1
while (n.isBranch) {
n = n.asBranch.down(0)
h += 1
}
h
}
}
final def top(implicit tx: T): Option[Node[T, A, E]] = Option(topN)
@inline protected final def topN(implicit tx: T): Node[T, A, E] = downNode()
final def debugPrint()(implicit tx: T): String = topN.printNode(isRight = true).mkString("\n")
final def toIndexedSeq(implicit tx: T): Vec [E] = fillBuilder(Vector.newBuilder)
final def toList (implicit tx: T): List[E] = fillBuilder(List .newBuilder)
final def toSeq (implicit tx: T): Seq [E] = fillBuilder(Seq .newBuilder)
final def toSet (implicit tx: T): ISet[E] = fillBuilder(ISet .newBuilder)
private[this] def fillBuilder[Res](b: mutable.Builder[E, Res])(implicit tx: T): Res = {
val i = iterator
while (i.hasNext) {
b += i.next() // Txn
}
b.result()
}
@tailrec
private[this] def headImpl(n: Node[T, A, E])(implicit tx: T): E = {
if (n.isLeaf) {
n.asLeaf.entry(0)
} else {
headImpl(n.asBranch.down(0))
}
}
final def head(implicit tx: T): E = {
val n0 = topN
if (n0 eq null) throw new NoSuchElementException("head of empty list")
else headImpl(n0)
}
final def headOption(implicit tx: T): Option[E] = {
val n0 = topN
if (n0 eq null) None
else Some(headImpl(n0))
}
@tailrec
private[this] def lastImpl(n: Node[T, A, E])(implicit tx: T): E = {
if (n.isLeaf) {
n.asLeaf.entry(n.size - 2) // N.B. we have `null` as terminator
} else {
lastImpl(n.asBranch.down(n.size - 1))
}
}
final def last(implicit tx: T): E = {
val n0 = topN
if (n0 eq null) throw new NoSuchElementException("last of empty list")
else lastImpl(n0)
}
final def lastOption(implicit tx: T): Option[E] = {
val n0 = topN
if (n0 eq null) None
else Some(lastImpl(n0))
}
/** Finds the leaf and index in the leaf corresponding to the entry that holds either the given
* search key or the greatest key in the set smaller than the search key.
*
* @param key the search key
* @return if `Some`, holds the leaf and index for the floor element (whose key is <= the search key),
* if `None`, there is no key smaller than or equal to the search key in the list
*/
final def floor(key: A)(implicit tx: T): Option[E] = {
// the algorithm is as follows: find the index of the search key in the current level.
// if the key was found, just go down straight to the leaf. if not:
// - the index points to an element greater than the search key
// - if the index is >0, let the key at index-1 be the backup-key, the node is the backup-node
// - if a leaf is reached and the index is 0
// - if no backup-node exists, return None
// - if a backup-node exists, follow that node straight down along the backup-key to the leaf
// In the worst case, we descend the list twice, still giving O(log n) performance, while
// saving the effort to horizontally connect the leaves.
@tailrec
def straight(n: Node[T, A, E], idx: Int): E = {
if (n.isLeaf) {
n.asLeaf.entry(idx)
} else {
val c = n.asBranch.down(idx)
straight(c, c.size - 1)
}
}
@tailrec
def step(n: Node[T, A, E], _bckNode: Node[T, A, E], _bckIdx: Int, isRight: Boolean): Option[E] = {
val idx = if (isRight) indexInNodeR(key, n) else indexInNodeL(key, n)
if (idx < 0) {
// found
Some(straight(n, -(idx + 1)))
} else {
// not found
var bckNode = _bckNode
var bckIdx = _bckIdx
if (idx > 0) {
// new backup exists, because there is an entry smaller than the search key
bckNode = n
bckIdx = idx - 1
}
if (n.isLeaf) {
if (bckNode eq null) None else Some(straight(bckNode, bckIdx))
} else {
step(n.asBranch.down(idx), bckNode, bckIdx, isRight && idx == n.size - 1)
}
}
}
val n0 = topN
if (n0 eq null) None else step(n0, null, 0, isRight = true)
}
/** Finds the leaf and index in the leaf corresponding to the entry that holds either the given
* search key or the smallest key in the set greater than the search key.
*
* @param key the search key
* @return if `Some`, holds the leaf and index for the ceiling element (whose key is >= the search key),
* if `None`, there is no key greater than or equal to the search key in the list
*/
final def ceil(key: A)(implicit tx: T): Option[E] = {
@tailrec
def step(n: Node[T, A, E], isRight: Boolean): Option[E] = {
val idx = if (isRight) indexInNodeR(key, n) else indexInNodeL(key, n)
val idxP = if (idx < 0) -(idx + 1) else idx
val newRight = isRight && idxP == n.size - 1
if (n.isLeaf) {
if (newRight) None else Some(n.asLeaf.entry(idxP))
} else {
step(n.asBranch.down(idxP), newRight)
}
}
val c = topN
if (c eq null) None else step(c, isRight = true)
}
final def isomorphicQuery(compare: A => Int)(implicit tx: T): (E, Int) = {
def isoIndexR(n: Node[T, A, E]): Int = {
var idx = 0
val sz = n.size - 1
while ({
val cmp = compare(n.key(idx))
if (cmp == 0) return -(idx + 1) else if (cmp < 0) return idx
idx += 1
idx < sz
}) ()
sz
}
def isoIndexL(n: Node[T, A, E]): Int = {
@tailrec
def step(idx: Int): Int = {
val cmp = compare(n.key(idx))
if (cmp == 0) -(idx + 1) else if (cmp < 0) idx else step(idx + 1)
}
step(0)
}
@tailrec
def stepRight(n: Node[T, A, E]): (E, Int) = {
val idx = isoIndexR(n)
val found = idx < 0
val idxP = if (found) -(idx + 1) else idx
if (n.isLeaf) {
val l = n.asLeaf
if (found) {
(l.entry(idxP), 0)
} else if (idxP == l.size - 1) {
(l.entry(idxP - 1), 1)
} else {
(l.entry(idxP), -1)
}
} else {
val c = n.asBranch.down(idxP)
if (idxP < n.size - 1) stepLeft(c) else stepRight(c)
}
}
@tailrec
def stepLeft(n: Node[T, A, E]): (E, Int) = {
val idx = isoIndexL(n)
val found = idx < 0
val idxP = if (found) -(idx + 1) else idx
if (n.isLeaf) {
val l = n.asLeaf
(l.entry(idxP), if (found) 0 else -1)
} else {
stepLeft(n.asBranch.down(idxP))
}
}
val c = topN
if (c eq null) {
throw new NoSuchElementException("isomorphicQuery on an empty list")
} else {
stepRight(c)
}
}
// ---- set support ----
final def contains(v: A)(implicit tx: T): Boolean = {
@tailrec
def stepRight(n: Node[T, A, E]): Boolean = {
val idx = indexInNodeR(v, n)
if (idx < 0) true
else if (n.isLeaf) false
else {
val c = n.asBranch.down(idx)
if (idx < n.size - 1) stepLeft(c) else stepRight(c)
}
}
@tailrec
def stepLeft(n: Node[T, A, E]): Boolean = {
val idx = indexInNodeL(v, n)
if (idx < 0) true else if (n.isLeaf) false else stepLeft(n.asBranch.down(idx))
}
val c = topN
if (c eq null) false else stepRight(c)
}
final /*protected */ def indexInNodeR(key: A, n: Node[T, A, E])(implicit tx: T): Int = {
var idx = 0
val sz = n.size - 1
while ({
val cmp = ordering.compare(key, n.key(idx))
if (cmp == 0) return -(idx + 1) else if (cmp < 0) return idx
idx += 1
idx < sz
}) ()
sz
}
final /* protected */ def indexInNodeL(key: A, n: Node[T, A, E])(implicit tx: T): Int = {
@tailrec
def step(idx: Int): Int = {
val cmp = ordering.compare(key, n.key(idx))
if (cmp == 0) -(idx + 1) else if (cmp < 0) idx else step(idx + 1)
}
step(0)
}
protected final def addEntry(key: A, entry: E)(implicit tx: T): Option[E] = {
val c = topN
if (c eq null) {
val l = newLeaf(entry)
downNode() = l
None
} else if (c.isLeaf) {
addToLeaf(key, entry, this /*head*/, 0, this /*head*/, 0, c.asLeaf, isRight = true)
} else {
addToBranch(key, entry, this /*head*/, 0, this /*head*/, 0, c.asBranch, isRight = true)
}
}
private[this] def addToLeaf(key: A, entry: E, pp: HeadOrBranch[T, A, E], ppIdx: Int, p: HeadOrBranch[T, A, E],
pIdx: Int, l: Leaf[T, A, E], isRight: Boolean)(implicit tx: T): Option[E] = {
val idx = if (isRight) indexInNodeR(key, l) else indexInNodeL(key, l)
if (idx < 0) {
val idxP = -(idx + 1)
val oldEntry = l.entry(idxP)
if (entry != oldEntry) {
val lNew = l.update(idxP, entry)
p.updateDown(pIdx, lNew)
}
Some(oldEntry)
} else {
if (l.size == arrMaxSz) {
val splitKey = l.key(minGap)
val tup = l.splitAndInsert(idx, entry)(this)
val left = tup._1
val right = tup._2
val pNew = p.insertAfterSplit(pIdx, splitKey, left, right, id)(tx, this)
pp.updateDown(ppIdx, pNew)
if (hasObserver) keyObserver.keyUp(splitKey)(tx)
} else {
val lNew = l.insert(idx, entry)
// and overwrite down entry in pn's parent
p.updateDown(pIdx, lNew)
}
None
}
}
@tailrec
private[this] def addToBranch(key: A, entry: E, pp: HeadOrBranch[T, A, E], ppIdx: Int,
p: HeadOrBranch[T, A, E], pIdx: Int, b: Branch[T, A, E], isRight: Boolean)
(implicit tx: T): Option[E] = {
val idx = if (isRight) indexInNodeR(key, b) else indexInNodeL(key, b)
val found = idx < 0
val idxP = if (found) -(idx + 1) else idx
var bNew = b
var idxNew = idxP
var pNew = p
var pIdxNew = pIdx
val bsz = b.size
val isRightNew = isRight && idxP == bsz - 1
if (!found && bsz == arrMaxSz) {
val splitKey = b.key(minGap)
val tup = b.split(this)
val left = tup._1
val right = tup._2
val pbNew = p.insertAfterSplit(pIdx, splitKey, left, right, id)(tx, this)
pNew = pbNew
pp.updateDown(ppIdx, pbNew)
val mns = arrMinSz
if (idx < mns) {
bNew = left
} else {
bNew = right
pIdxNew += 1
idxNew -= mns
}
if (hasObserver) keyObserver.keyUp(splitKey)(tx)
}
val c = bNew.down(idxNew)
if (c.isLeaf) {
addToLeaf (key, entry, pNew, pIdxNew, bNew, idxNew, c.asLeaf, isRightNew)
} else {
addToBranch(key, entry, pNew, pIdxNew, bNew, idxNew, c.asBranch, isRightNew)
}
}
final def -=(key: A)(implicit tx: T): this.type = {
removeEntry(key); this
}
protected final def removeEntry(key: A)(implicit tx: T): Option[E] = {
val c = topN
if (c eq null) {
None
} else if (c.isLeaf) {
removeFromLeaf (key, downNode, c.asLeaf , isRight = true, lDirty = false)
} else {
removeFromBranch(key, downNode, c.asBranch, isRight = true, bDirty = false)
}
}
private[this] def removeFromLeaf(key: A, pDown: Sink[T, Node[T, A, E]], l: Leaf[T, A, E],
isRight: Boolean, lDirty: Boolean)(implicit tx: T): Option[E] = {
val idx = if (isRight) indexInNodeR(key, l) else indexInNodeL(key, l)
val found = idx < 0
if (found) {
val idxP = -(idx + 1)
val lNew = l.removeColumn(idxP)(this)
pDown() = if (lNew.size > 1) lNew else null
Some(l.entry(idxP))
} else {
if (lDirty) {
pDown() = if (l.size > 1) l else null
}
None
}
}
@tailrec
private[this] def removeFromBranchAndBubble(key: A, pDown: Sink[T, Node[T, A, E]], b: Branch[T, A, E],
leafUpKey: A)(implicit tx: T): Option[E] = {
val bsz = b.size
val idxP = bsz - 1 // that we know
val mns = arrMinSz
val c = b.down(idxP)
val cSz = c.size
var bNew = null: Branch[T, A, E]
var bDownIdx = idxP
var cNew = c
if (hasObserver) keyObserver.keyDown(key)(tx)
// a merge or borrow is necessary either when we descend
// to a minimally filled child (because that child might
// need to shrink in the next step)
if (cSz == mns) {
// merge with or borrow from the left
val idxPM1 = idxP - 1
val cSib = b.down(idxPM1)
val cSibSz = cSib.size
val downKey = b.key(idxPM1)
if (hasObserver) keyObserver.keyDown(downKey)(tx)
if (cSibSz == mns) {
// merge with the left
// The parent needs to remove the
// entry of the left sibling.
val bNew0 = b.removeColumn(idxPM1)(this)
bNew = bNew0.updateKey(idxPM1, leafUpKey) // XXX optimise by merging with previous operation
b.downRef(idxPM1).dispose()
bDownIdx = idxPM1
cNew = c.mergeLeft(cSib)
} else {
// borrow from the left
// the parent needs to update the key for the
// left sibling to match the before-last key in
// the left sibling.
val upKey = cSib.key(cSibSz - 2)
val bNew0 = b.updateKey(idxPM1, upKey)
bNew = bNew0.updateKey(idxP, leafUpKey) // XXX optimise by merging with previous operation
if (hasObserver) keyObserver.keyUp(upKey)(tx)
val bDown1 = b.downRef(idxPM1)
bDown1() = cSib.removeColumn(cSibSz - 1)(this)
cNew = c.borrowLeft(cSib)
}
} else {
bNew = b.updateKey(idxP, leafUpKey)
}
if (hasObserver) keyObserver.keyUp(leafUpKey)(tx)
// branch changed
val bDown = if (bNew.size > 1) {
pDown() = bNew // update down ref from which it came
bNew.downRef(bDownIdx)
} else {
// unfortunately we do not have `p`
// assert( p == Head )
bNew.downRef(0).dispose()
pDown
}
if (cNew.isLeaf) {
removeFromLeaf (key, bDown, cNew.asLeaf, isRight = false, cNew ne c)
} else {
removeFromBranchAndBubble(key, bDown, cNew.asBranch, leafUpKey)
}
}
@tailrec
private[this] def removeFromBranch(key: A, pDown: Sink[T, Node[T, A, E]], b: Branch[T, A, E],
isRight: Boolean, bDirty: Boolean)(implicit tx: T): Option[E] = {
val idx = if (isRight) indexInNodeR(key, b) else indexInNodeL(key, b)
val found = idx < 0
val idxP = if (found) -(idx + 1) else idx
val bsz = b.size
val mns = arrMinSz
val c = b.down(idxP)
val cSz = /* if( cFound ) c.size - 1 else */ c.size
// if v is found, it will appear in right-most position in all following children.
// there are two possibilities:
// (1) a borrow-from-right or merge-with-right is performed. in this case,
// v is overwritten in the current branch, thus keep going normally
// (no need to specially treat the branch).
// (2) none of these two operations are performed (because either the child size
// is greater than minimum, or v appears in right-most position in b (causing a left op).
// -- is this second case possible? no, because we would have encountered
// case (1) in the previous iteration, that is, a borrow-from-right or merge-
// with-right would have been performed, and thus v cannot appear in right-most
// position, and there cannot be a left op.
// Therefore, we only need to specially treat case (2), that is `cSz > mns`!
if (found && cSz > mns) {
// we are here, because the key was found and it would appear in the right-most position
// in the child, unless we treat it specially here, by finding the key that will bubble
// up!
@tailrec def findUpKey(n: Node[T, A, E]): A = {
if (n.isLeaf) {
n.key(n.size - 2)
} else {
findUpKey(n.asBranch.down(n.size - 1))
}
}
val leafUpKey = findUpKey(c)
if (hasObserver) keyObserver.keyDown(key)(tx)
val bNew = b.updateKey(idxP, leafUpKey)
if (hasObserver) keyObserver.keyUp(leafUpKey)(tx)
pDown() = bNew // update down ref from which we came
val bDown = bNew.downRef(idxP)
return if (c.isLeaf) {
removeFromLeaf (key, bDown, c.asLeaf, isRight = false, lDirty = false)
} else {
removeFromBranchAndBubble(key, bDown, c.asBranch, leafUpKey)
}
}
var isRightNew = isRight && idxP == bsz - 1
var bNew = b
var bDownIdx = idxP
var cNew = c
// a merge or borrow is necessary either when we descend
// to a minimally filled child (because that child might
// need to shrink in the next step)
if (cSz == mns) {
val idxP1 = idxP + 1
val bHasRight = idxP1 < bsz
if (bHasRight) {
// merge with or borrow from/to the right
val cSib = b.down(idxP1)
val cSibSz = cSib.size
val mergedSz = cSz + cSibSz
val downKey = b.key(idxP)
if (hasObserver) keyObserver.keyDown(downKey)(tx)
if (mergedSz <= arrMaxSz) {
// merge with the right
// remove the entry at idxP from the branch,
// and actualise b with virtual sibling. the key
// at bNew's index idxP is now the one formerly at
// idxP1, hence the right-most key in cSib.
bNew = b.removeColumn(idxP)(this)
b.downRef(idxP).dispose()
cNew = c.mergeRight(cSib)
isRightNew = isRight && idxP == bsz - 2 // ! we might be in the right-most branch now
} else {
// borrow from the right
assert(cSibSz > mns)
// update the key index idxP of the
// originating sibling to match the first key in
// the right sibling
val upKey = cSib.key(0)
bNew = b.updateKey(idxP, upKey)
if (hasObserver) keyObserver.keyUp(upKey)(tx)
val bDown1 = b.downRef(idxP1)
bDown1() = cSib.removeColumn(0)(this)
cNew = c.borrowRight(cSib)
}
} else { // merge with or borrow from the left
// it implies that if cFound is true, cIdx is < c.size - 1
// that is, the key is not in the last element of c
// (because otherwise, b would have already in its
// virtualization be merged to or have borrowed from its right sibling)
val idxPM1 = idxP - 1
val cSib = b.down(idxPM1)
val cSibSz = cSib.size
val downKey = b.key(idxPM1)
if (hasObserver) keyObserver.keyDown(downKey)(tx)
if (cSibSz == mns) { // merge with the left
// The parent needs to remove the
// entry of the left sibling.
bNew = b.removeColumn(idxPM1)(this)
b.downRef(idxPM1).dispose()
bDownIdx = idxPM1
cNew = c.mergeLeft(cSib)
} else { // borrow from the left
// the parent needs to update the key for the
// left sibling to match the before-last key in
// the left sibling.
val upKey = cSib.key(cSibSz - 2)
bNew = b.updateKey(idxPM1, upKey)
if (hasObserver) keyObserver.keyUp(upKey)(tx)
val bDown1 = b.downRef(idxPM1)
bDown1() = cSib.removeColumn(cSibSz - 1)(this)
cNew = c.borrowLeft(cSib)
}
}
}
val bDown = if (bDirty || (bNew ne b)) { // branch changed
if (bNew.size > 1) {
pDown() = bNew // update down ref from which it came
bNew.downRef(bDownIdx)
} else {
// unfortunately we do not have `p`
// assert( p == Head )
bNew.downRef(0).dispose()
pDown
}
} else {
bNew.downRef(bDownIdx)
}
// val bDown = bNew.downRef( bDownIdx )
val cDirty = cNew ne c
if (cNew.isLeaf) {
removeFromLeaf (key, bDown, cNew.asLeaf , isRight = isRightNew, lDirty = cDirty)
} else {
removeFromBranch(key, bDown, cNew.asBranch, isRight = isRightNew, bDirty = cDirty)
}
}
final def iterator(implicit tx: T): Iterator[E] = {
val i = new EntryIteratorImpl
i.init()
i
}
// ---- TFormat[T, Node[S, A]] ----
override def write(v: Node[T, A, E], out: DataOutput): Unit =
if (v eq null) {
out.writeByte(0) // Bottom
} else {
v.write(out)(this)
}
override def readT(in: DataInput)(implicit tx: T): Node[T, A, E] = {
(in.readByte(): @switch) match {
case 0 => null // .asInstanceOf[ Branch[ S, A ]]
case 1 => Branch.read(in, isRight = false, id = id)(tx, this)
case 2 => readLeaf (in, isRight = false)
case 5 => Branch.read(in, isRight = true , id = id)(tx, this)
case 6 => readLeaf (in, isRight = true )
}
}
private[this] final class EntryIteratorImpl(implicit tx: T) extends IteratorImpl[E] {
protected def getValue(l: Leaf[T, A, E], idx: Int): E = l.entry(idx)
override def toString = "Iterator"
}
protected sealed abstract class IteratorImpl[C](implicit tx: T) extends Iterator[C] {
private[this] var l: Leaf[T, A, E] = _
private[this] var nextValue: C = _
private[this] var isRight = true
private[this] var idx = 0
private[this] var stack = List.empty[(Branch[T, A, E], Int, Boolean)]
override def toString = s"$impl.iterator"
protected def getValue(l: Leaf[T, A, E], idx: Int): C
@tailrec
private[this] def pushDown(n: Node[T, A, E], idx0: Int, r: Boolean): Unit =
if (n.isLeaf) {
val l2 = n.asLeaf
l = l2
idx = 0
isRight = r
nextValue = getValue(l2, 0) // l2.key( 0 )
} else {
val b = n.asBranch
stack ::= ((b, idx0 + 1, r))
pushDown(b.down(idx0), 0, r && idx0 == b.size - 1)
}
def init(): Unit = {
val c = topN
if (c ne null) pushDown(c, 0, r = true)
}
def hasNext: Boolean = l ne null // ordering.nequiv( nextKey, maxKey )
def next(): C = {
if (!hasNext) throw new java.util.NoSuchElementException("next on empty iterator")
val res = nextValue
idx += 1
if (idx == (if (isRight) l.size - 1 else l.size) /* || ordering.equiv( l.key( idx ), maxKey ) */ ) {
@tailrec def popUp(): Unit =
if (stack.isEmpty) {
l = null
nextValue = null.asInstanceOf[C] // maxKey
} else {
val (b, i, r) :: tail = stack: @unchecked
stack = tail
if (i < b.size) {
pushDown(b, i, r)
} else {
popUp()
}
}
popUp()
} else {
nextValue = getValue(l, idx) // l.key( idx )
}
res
}
}
def updateDown(i: Int, n: Node[T, A, E])(implicit tx: T): Unit = {
if (i != 0) throw new IndexOutOfBoundsException(i.toString)
downNode() = n
}
def insertAfterSplit(pIdx: Int, splitKey: A, left: Node[T, A, E], right: Node[T, A, E], id: Ident[T])
(implicit tx: T, head: Impl[T, A, E]): Branch[T, A, E] = {
val bKeys = Vector[A](splitKey, null.asInstanceOf[A])
val bDowns = Vector[Var[T, Node[T, A, E]]](
id.newVar(left),
id.newVar(right),
)
new Branch[T, A, E](bKeys, bDowns) // new parent branch
}
}
sealed trait HeadOrBranch[T <: Exec[T], A, E] {
private[HASkipList] def updateDown(i: Int, n: Node[T, A, E])(implicit tx: T): Unit
private[HASkipList] def insertAfterSplit(pIdx: Int, splitKey: A, left: Node[T, A, E], right: Node[T, A, E],
id: Ident[T])(implicit tx: T, list: Impl[T, A, E]): Branch[T, A, E]
}
sealed trait Node[T <: Exec[T], A, E] {
private[HASkipList] def removeColumn(idx: Int)(implicit /*tx: T,*/ list: Impl[T, A, E]): Node[T, A, E]
def size: Int
def key(i: Int): A
private[HASkipList] def write(out: DataOutput)(implicit list: Impl[T, A, E]): Unit
private[HASkipList] def leafSizeSum(implicit tx: T): Int
private[HASkipList] def printNode(isRight: Boolean)(implicit tx: T): Vec[String]
/*
* In merge-with-right, the right sibling's
* identifier is re-used for the merged node.
* Thus after the merge, the originating sibling
* should be disposed (when using an ephemeral
* data store). The parent needs to remove the
* entry of the originating sibling.
*
* (thus the disposal corresponds with the ref
* removed from the `downs` array)
*/
private[HASkipList] def mergeRight(sib: Node[T, A, E]): Node[T, A, E]
/*
* In borrow-from-right, both parents' downs need
* update, but identifiers are kept.
* the parent needs to update the key for the
* originating sibling to match the first key in
* the right sibling (or the new last key in the
* originating sibling).
*/
private[HASkipList] def borrowRight(sib: Node[T, A, E]): Node[T, A, E]
/*
* In merge-with-left, the originating sibling's
* identifier is re-used for the merged node.
* Thus after the merge, the left sibling
* should be disposed (when using an ephemeral
* data store). The parent needs to remove the
* entry of the left sibling.
*
* (thus the disposal corresponds with the ref
* removed from the `downs` array)
*/
private[HASkipList] def mergeLeft(sib: Node[T, A, E]): Node[T, A, E]
/*
* In borrow-from-left, both parents' downs need
* update, but identifiers are kept.
* the parent needs to update the key for the
* left sibling to match the before-last key in
* the left sibling.
*/
private[HASkipList] def borrowLeft(sib: Node[T, A, E]): Node[T, A, E]
def isLeaf: Boolean
def isBranch: Boolean
def asLeaf: Leaf [T, A, E]
def asBranch: Branch[T, A, E]
}
private final class SetLeaf[T <: Exec[T], A](private[HASkipList] val entries: Vector[A])
extends Leaf[T, A, A] {
protected override def copy(newEntries: Vector[A]): Leaf[T, A, A] = new SetLeaf(newEntries)
override def key(idx: Int): A = entries(idx)
}
private final class MapLeaf[T <: Exec[T], A, B](private[HASkipList] val entries: Vector[(A, B)])
extends Leaf[T, A, (A, B)] {
protected override def copy(newEntries: Vector[(A, B)]): Leaf[T, A, (A, B)] = new MapLeaf(newEntries)
override def key(idx: Int): A = entries(idx)._1
}
sealed trait Leaf[T <: Exec[T], A, E] extends Node[T, A, E] {
override def toString: String = entries.mkString("Leaf(", ",", ")")
private[HASkipList] def entries: Vector[E]
final def entry(idx: Int): E = entries(idx)
protected def copy(newEntries: Vector[E]): Leaf[T, A, E]
final override def size: Int = entries.size
final override def isLeaf: Boolean = true
final override def isBranch: Boolean = false
final override def asLeaf: Leaf[T, A, E] = this
final override def asBranch: Branch[T, A, E] = opNotSupported
private[HASkipList] final def leafSizeSum(implicit tx: T): Int = size
private[HASkipList] final def printNode(isRight: Boolean)(implicit tx: T): Vec[String] = {
val sz = size
val szm = sz - 1
val strings = Seq.tabulate(sz)(idx => if (!isRight || idx < szm) entry(idx).toString else "M")
Vector(strings.mkString("--"))
}
private[HASkipList] final def mergeRight(sib: Node[T, A, E]): Node[T, A, E] = {
val lSib = sib.asLeaf
copy(entries ++ lSib.entries)
}
private[HASkipList] final def borrowRight(sib: Node[T, A, E]): Node[T, A, E] = {
val lSib = sib.asLeaf
copy(entries :+ lSib.entries.head)
}
private[HASkipList] final def mergeLeft(sib: Node[T, A, E]): Node[T, A, E] = {
val lSib = sib.asLeaf
copy(lSib.entries ++ entries)
}
private[HASkipList] final def borrowLeft(sib: Node[T, A, E]): Node[T, A, E] = {
val lSib = sib.asLeaf
copy(lSib.entries.last +: entries)
}
private[HASkipList] final def insert(idx: Int, entry: E)/*(implicit list: Impl[T, A, E])*/: Leaf[T, A, E] = {
val newEntries = entries.patch(idx, Vector(entry), 0)
copy(newEntries)
}
private[HASkipList] final def update(idx: Int, entry: E)/*(implicit list: Impl[T, A, E])*/: Leaf[T, A, E] = {
val newEntries = entries.patch(idx, Vector(entry), 1)
copy(newEntries)
}
private[HASkipList] final def splitAndInsert(idx: Int, entry: E)
(implicit list: Impl[T, A, E]): (Leaf[T, A, E], Leaf[T, A, E]) = {
// assert( size == arrMaxSz )
val arrMinSz = list.arrMinSz
val (len0, ren0) = entries.splitAt(arrMinSz)
if (idx < arrMinSz) {
// split and add `v` to left leaf
val len = len0.patch(idx, Vector(entry), 0)
val left = copy(len)
val right = copy(ren0)
(left, right)
} else {
// split and add `v` to right leaf
val numL = idx - arrMinSz
val ren = ren0.patch(numL, Vector(entry), 0)
val left = copy(len0)
val right = copy(ren)
(left, right)
}
}
private[HASkipList] final def removeColumn(idx: Int)(implicit /*tx: T,*/ list: Impl[T, A, E]): Leaf[T, A, E] = {
val newEntries = entries.patch(idx, Vector.empty, 1)
copy(newEntries)
}
private[HASkipList] final def write(out: DataOutput)(implicit list: Impl[T, A, E]): Unit = {
val sz = size
val sz1 = sz - 1
val isRight = entries(sz1) == null // XXX XXX
val szi = if (isRight) sz1 else sz
out.writeByte(if (isRight) 6 else 2)
out.writeByte(sz)
var i = 0
while (i < szi) {
list.writeEntry(entries(i), out)
i += 1
}
}
}
object Branch {
private[HASkipList] def read[T <: Exec[T], A, B](in: DataInput, isRight: Boolean, id: Ident[T])
(implicit tx: T, list: Impl[T, A, B]): Branch[T, A, B] = {
import list.keyFormat
val sz = in.readByte().toInt
val szi = if (isRight) sz - 1 else sz
val keys = Vector.tabulate(sz) { i =>
if (i < szi) keyFormat.readT(in) else null.asInstanceOf[A]
}
val downs = Vector.fill(sz)(id.readVar[Node[T, A, B]](in))
new Branch[T, A, B](keys, downs)
}
}
final class Branch[T <: Exec[T], A, B](private[HASkipList] val keys : Vector[A],
private[HASkipList] val downs: Vector[Var[T, Node[T, A, B]]])
extends HeadOrBranch[T, A, B] with Node[T, A, B] {
override def toString: String = keys.mkString("Branch(", ",", ")")
override def isLeaf: Boolean = false
override def isBranch: Boolean = true
override def asLeaf: Leaf [T, A, B] = opNotSupported
override def asBranch: Branch[T, A, B] = this
private[HASkipList] def mergeRight(sib: Node[T, A, B]): Node[T, A, B] = {
val bSib = sib.asBranch
new Branch[T, A, B](keys ++ bSib.keys, downs ++ bSib.downs)
}
private[HASkipList] def borrowRight(sib: Node[T, A, B]): Node[T, A, B] = {
val bSib = sib.asBranch
new Branch[T, A, B](keys :+ bSib.keys.head, downs :+ bSib.downs.head)
}
private[HASkipList] def mergeLeft(sib: Node[T, A, B]): Node[T, A, B] = {
val bSib = sib.asBranch
new Branch[T, A, B](bSib.keys ++ keys, bSib.downs ++ downs)
}
private[HASkipList] def borrowLeft(sib: Node[T, A, B]): Node[T, A, B] = {
val bSib = sib.asBranch
new Branch[T, A, B](bSib.keys.last +: keys, bSib.downs.last +: downs)
}
private[HASkipList] def leafSizeSum(implicit tx: T): Int = {
var res = 0
val sz = size
var i = 0; while (i < sz) {
res += down(i).leafSizeSum
i += 1
}
res
}
private[HASkipList] def printNode(isRight: Boolean)(implicit tx: T): Vec[String] = {
val sz = size
val szm = sz - 1
val columns = Vector.tabulate(sz) { idx =>
val rr = isRight && idx == szm
val child = down(idx).printNode(rr)
val childSz = child.head.length()
val ks = if (rr) "M" else key(idx).toString
val keySz = ks.length()
val colSz = math.max(keySz, childSz) + 2
val keyAdd = (if (idx == size - 1) " " else "-") * (colSz - keySz)
val bar = s"|${" " * (colSz - 1)}"
val childAdd = " " * (colSz - childSz)
Vector(ks + keyAdd, bar) ++ child.map(_ + childAdd)
}
Vector.tabulate(columns.map(_.size).max) { row =>
columns.map(_.apply(row)).mkString("")
}
}
override def key(idx: Int): A = keys(idx)
override def size: Int = keys.length
private[HASkipList] def downRef(i: Int): Var[T, Node[T, A, B]] = downs(i)
def down(i: Int)(implicit tx: T): Node[T, A, B] = downs(i)()
private[HASkipList] def split(implicit /*tx: T,*/ list: Impl[T, A, B]): (Branch[T, A, B], Branch[T, A, B]) = {
val lsz = list.arrMinSz
val (lKeys , rKeys ) = keys .splitAt(lsz)
val (lDowns, rDowns) = downs.splitAt(lsz)
val left = new Branch[T, A, B](lKeys, lDowns)
val right = new Branch[T, A, B](rKeys, rDowns)
(left, right)
}
private[HASkipList] def updateDown(i: Int, n: Node[T, A, B])(implicit tx: T): Unit = downs(i)() = n
private[HASkipList] def removeColumn(idx: Int)(implicit list: Impl[T, A, B]): Branch[T, A, B] = {
val newKeys = keys .patch(idx, Vector.empty, 1)
val newDowns = downs.patch(idx, Vector.empty, 1)
new Branch[T, A, B](newKeys, newDowns)
}
private[HASkipList] def updateKey(idx: Int, key: A)/*(implicit tx: T, list: Impl[T, A, B])*/: Branch[T, A, B] = {
val newKeys = keys.updated(idx, key)
new Branch[T, A, B](newKeys, downs)
}
private[HASkipList] def insertAfterSplit(idx: Int, splitKey: A, left: Node[T, A, B], right: Node[T, A, B],
id: Ident[T])(implicit tx: T, list: Impl[T, A, B]): Branch[T, A, B] = {
// we must make a copy of this branch with the
// size increased by one. the new key is `splitKey`
// which gets inserted at the index where we went
// down, `idx`.
val bKeys = keys.patch (idx, Vector(splitKey), 0)
val bDowns = downs.patch(idx, Vector(id.newVar(left)), 0)
// copy entries right to split index
val rightOff = idx + 1
bDowns(rightOff)() = right
new Branch[T, A, B](bKeys, bDowns)
}
private[HASkipList] def write(out: DataOutput)(implicit list: Impl[T, A, B]): Unit = {
import list.keyFormat
val sz = size
val sz1 = sz - 1
val isRight = keys(sz1) == null
val szi = if (isRight) sz1 else sz
out.writeByte(if (isRight) 5 else 1)
out.writeByte(sz)
var i = 0; while (i < szi) {
keyFormat.write(keys(i), out)
i += 1
}
i = 0; while (i < sz) {
downs(i).write(out)
i += 1
}
}
}
object Set {
type Node [T <: Exec[T], A] = HASkipList.Node [T, A, A]
type Branch[T <: Exec[T], A] = HASkipList.Branch[T, A, A]
type Leaf [T <: Exec[T], A] = HASkipList.Leaf [T, A, A]
/** Creates a new empty skip list with default minimum gap parameter of `2` and no key observer.
* Type parameter `S` specifies the STM system to use. Type parameter `A`
* specifies the type of the keys stored in the list.
*
* @param tx the transaction in which to initialize the structure
* @param ord the ordering of the keys. This is an instance of `txn.Ordering` to allow
* for specialized versions and transactional restrictions.
* @param keyFormat the format for the elements, in case a persistent STM is used.
*/
def empty[T <: Exec[T], A](implicit tx: T, ord: TOrdering[T, A],
keyFormat: TFormat[T, A]): HASkipList.Set[T, A] =
empty()
/** Creates a new empty skip list. Type parameter `S` specifies the STM system to use. Type parameter `A`
* specifies the type of the keys stored in the list.
*
* @param minGap the minimum gap-size used for the skip list. This value must be between 1 and 126 inclusive.
* @param keyObserver an object which observes key promotions and demotions. Use `NoKeyObserver` (default) if
* key motions do not need to be monitored. The monitoring allows the use of the skip list
* for synchronized decimation of related data structures, such as the deterministic
* skip quadtree.
* @param tx the transaction in which to initialize the structure
* @param ord the ordering of the keys. This is an instance of `txn.Ordering` to allow
* for specialized versions and transactional restrictions.
* @param keyFormat the format for the elements, in case a persistent STM is used.
*/
def empty[T <: Exec[T], A](minGap: Int = 2,
keyObserver: SkipList.KeyObserver[T, A] = SkipList.NoKeyObserver)
(implicit tx: T, ord: TOrdering[T, A],
keyFormat: TFormat[T, A]): HASkipList.Set[T, A] = {
// 255 <= arrMaxSz = (minGap + 1) << 1
// ; this is, so we can write a node's size as signed byte, and
// no reasonable app would use a node size > 255
if (minGap < 1 || minGap > 126) sys.error(s"Minimum gap ($minGap) cannot be less than 1 or greater than 126")
val implId = tx.newId()
new SetImpl[T, A](implId, minGap, keyObserver, list => {
implId.newVar[Node[T, A]](null)(tx, list)
})
}
def read[T <: Exec[T], A](in: DataInput, keyObserver: SkipList.KeyObserver[T, A] = SkipList.NoKeyObserver)
(implicit tx: T, ordering: TOrdering[T, A],
keyFormat: TFormat[T, A]): HASkipList.Set[T, A] = {
val id = tx.readId(in)
val version = in.readByte()
if (version != SER_VERSION)
sys.error(s"Incompatible serialized version (found $version, required $SER_VERSION).")
val minGap = in.readByte().toInt
new SetImpl[T, A](id, minGap, keyObserver, list => id.readVar[Node[T, A]](in)(list))
}
def format[T <: Exec[T], A](keyObserver: SkipList.KeyObserver[T, A] = SkipList.NoKeyObserver)
(implicit ordering: TOrdering[T, A],
keyFormat: TFormat[T, A]): TFormat[T, HASkipList.Set[T, A]] =
new SetFmt[T, A](keyObserver)
}
trait Set[T <: Exec[T], A] extends SkipList.Set[T, A] with HASkipList[T, A, A]
object Map {
type Node [T <: Exec[T], A, B] = HASkipList.Node [T, A, (A, B)]
type Branch[T <: Exec[T], A, B] = HASkipList.Branch[T, A, (A, B)]
type Leaf [T <: Exec[T], A, B] = HASkipList.Leaf [T, A, (A, B)]
/** Creates a new empty skip list with default minimum gap parameter of `2` and no key observer.
* Type parameter `S` specifies the STM system to use. Type parameter `A`
* specifies the type of the keys stored in the list.
*
* @param tx the transaction in which to initialize the structure
* @param ord the ordering of the keys. This is an instance of `txn.Ordering` to allow
* for specialized versions and transactional restrictions.
* @param keyFormat the format for the elements, in case a persistent STM is used.
*/
def empty[T <: Exec[T], A, B](implicit tx: T, ord: Ordering[A],
keyFormat: TFormat[T, A],
valueFormat: TFormat[T, B]): HASkipList.Map[T, A, B] =
empty()
/** Creates a new empty skip list. Type parameter `S` specifies the STM system to use. Type parameter `A`
* specifies the type of the keys stored in the list.
*
* @param minGap the minimum gap-size used for the skip list. This value must be between 1 and 126 inclusive.
* @param keyObserver an object which observes key promotions and demotions. Use `NoKeyObserver` (default) if
* key motions do not need to be monitored. The monitoring allows the use of the skip list
* for synchronized decimation of related data structures, such as the deterministic
* skip quadtree.
* @param tx the transaction in which to initialize the structure
* @param ord the ordering of the keys. This is an instance of `txn.Ordering` to allow
* for specialized versions and transactional restrictions.
* @param keyFormat the format for the elements, in case a persistent STM is used.
*/
def empty[T <: Exec[T], A, B](minGap: Int = 2,
keyObserver: SkipList.KeyObserver[T, A] = SkipList.NoKeyObserver)
(implicit tx: T, ord: Ordering[A],
keyFormat: TFormat[T, A],
valueFormat: TFormat[T, B]): HASkipList.Map[T, A, B] = {
// 255 <= arrMaxSz = (minGap + 1) << 1
// ; this is, so we can write a node's size as signed byte, and
// no reasonable app would use a node size > 255
if (minGap < 1 || minGap > 126) sys.error(s"Minimum gap ($minGap) cannot be less than 1 or greater than 126")
val implId = tx.newId()
new MapImpl[T, A, B](implId, minGap, keyObserver, list => {
implId.newVar[Node[T, A, B]](null)(tx, list)
})
}
def read[T <: Exec[T], A, B](in: DataInput, keyObserver: SkipList.KeyObserver[T, A] = SkipList.NoKeyObserver)
(implicit tx: T, ordering: TOrdering[T, A],
keyFormat: TFormat[T, A],
valueFormat: TFormat[T, B]): HASkipList.Map[T, A, B] = {
val id = tx.readId(in)
val version = in.readByte()
if (version != SER_VERSION) sys.error(s"Incompatible serialized version (found $version, required $SER_VERSION).")
val minGap = in.readByte().toInt
new MapImpl[T, A, B](id, minGap, keyObserver, list => id.readVar[Node[T, A, B]](in)(list))
}
def format[T <: Exec[T], A, B](keyObserver: SkipList.KeyObserver[T, A] = SkipList.NoKeyObserver)
(implicit ordering: TOrdering[T, A],
keyFormat: TFormat[T, A],
valueFormat: TFormat[T, B]): TFormat[T, HASkipList.Map[T, A, B]] =
new MapFmt[T, A, B](keyObserver)
}
trait Map[T <: Exec[T], A, B]
extends SkipList.Map[T, A, B] with HASkipList[T, A, (A, B)]
}
trait HASkipList[T <: Exec[T], A, E] extends SkipList[T, A, E] {
def top(implicit tx: T): Option[HASkipList.Node[T, A, E]]
/** Finds the right-most key which
* is greater than or equal to the query key.
*
* @param key the key to search for
* @param n the branch or leaf from which to go down
*
* @return the index to go down (a node whose key is greater than `key`),
* or `-(index+1)` if `key` was found at `index`
*/
def indexInNodeR(key: A, n: HASkipList.Node[T, A, E])(implicit tx: T): Int
def indexInNodeL(key: A, n: HASkipList.Node[T, A, E])(implicit tx: T): Int
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy