diff --git a/mcas/js/src/main/scala/dev/tauri/choam/internal/mcas/LogMap2.scala b/mcas/js/src/main/scala/dev/tauri/choam/internal/mcas/LogMap2.scala index c42d1f10..a8c0906d 100644 --- a/mcas/js/src/main/scala/dev/tauri/choam/internal/mcas/LogMap2.scala +++ b/mcas/js/src/main/scala/dev/tauri/choam/internal/mcas/LogMap2.scala @@ -32,12 +32,6 @@ private[mcas] final class LogMap2[A] private ( final def definitelyReadOnly: Boolean = this.isBlueSubtree - protected final override def hashOf(k: MemoryLocation[A]): Long = - k.id - - protected final override def keyOf(a: LogEntry[A]): MemoryLocation[A] = - a.address - protected final override def isBlue(a: LogEntry[A]): Boolean = a.readOnly diff --git a/mcas/js/src/main/scala/dev/tauri/choam/internal/mcas/WdLike.scala b/mcas/js/src/main/scala/dev/tauri/choam/internal/mcas/WdLike.scala index 6c166b8f..99448d4e 100644 --- a/mcas/js/src/main/scala/dev/tauri/choam/internal/mcas/WdLike.scala +++ b/mcas/js/src/main/scala/dev/tauri/choam/internal/mcas/WdLike.scala @@ -21,11 +21,14 @@ package mcas import scala.util.hashing.MurmurHash3 -sealed abstract class WdLike[A] { +sealed abstract class WdLike[A] extends Hamt.HasKey[MemoryLocation[A]] { val address: MemoryLocation[A] val ov: A val nv: A val oldVersion: Long + + final override def key: MemoryLocation[A] = + this.address } final class LogEntry[A] private ( // formerly called HWD diff --git a/mcas/jvm/src/main/scala/dev/tauri/choam/internal/mcas/LogMap2.scala b/mcas/jvm/src/main/scala/dev/tauri/choam/internal/mcas/LogMap2.scala index ecbca937..7d66b649 100644 --- a/mcas/jvm/src/main/scala/dev/tauri/choam/internal/mcas/LogMap2.scala +++ b/mcas/jvm/src/main/scala/dev/tauri/choam/internal/mcas/LogMap2.scala @@ -36,12 +36,6 @@ private[mcas] final class LogMap2[A] private[mcas] ( final def definitelyReadOnly: Boolean = this.isBlueSubtree - protected final override def hashOf(k: MemoryLocation[A]): Long = - k.id - - protected final override def keyOf(a: LogEntry[A]): MemoryLocation[A] = - a.address - protected final override def isBlue(a: LogEntry[A]): Boolean = a.readOnly diff --git a/mcas/jvm/src/main/scala/dev/tauri/choam/internal/mcas/LogMapMut.scala b/mcas/jvm/src/main/scala/dev/tauri/choam/internal/mcas/LogMapMut.scala index 546465d2..53db65cb 100644 --- a/mcas/jvm/src/main/scala/dev/tauri/choam/internal/mcas/LogMapMut.scala +++ b/mcas/jvm/src/main/scala/dev/tauri/choam/internal/mcas/LogMapMut.scala @@ -31,12 +31,6 @@ private[mcas] final class LogMapMut[A] private ( this.forAll(ctx) } - protected final override def keyOf(a: LogEntry[A]): MemoryLocation[A] = - a.address - - protected final override def hashOf(k: MemoryLocation[A]): Long = - k.id - protected final override def isBlue(a: LogEntry[A]): Boolean = a.readOnly diff --git a/mcas/jvm/src/main/scala/dev/tauri/choam/internal/mcas/WdLike.scala b/mcas/jvm/src/main/scala/dev/tauri/choam/internal/mcas/WdLike.scala index e1dbfb55..80dc6942 100644 --- a/mcas/jvm/src/main/scala/dev/tauri/choam/internal/mcas/WdLike.scala +++ b/mcas/jvm/src/main/scala/dev/tauri/choam/internal/mcas/WdLike.scala @@ -21,12 +21,15 @@ package mcas import scala.util.hashing.MurmurHash3 -sealed trait WdLike[A] { +sealed trait WdLike[A] extends Hamt.HasKey[MemoryLocation[A]] { val address: MemoryLocation[A] def ov: A def nv: A val oldVersion: Long def cleanForGc(wasSuccessful: Boolean, sentinel: A): Unit + + final override def key: MemoryLocation[A] = + this.address } // TODO: this is duplicated on JS diff --git a/mcas/shared/src/main/scala/dev/tauri/choam/internal/mcas/AbstractHamt.scala b/mcas/shared/src/main/scala/dev/tauri/choam/internal/mcas/AbstractHamt.scala index a1d8239f..f499bd71 100644 --- a/mcas/shared/src/main/scala/dev/tauri/choam/internal/mcas/AbstractHamt.scala +++ b/mcas/shared/src/main/scala/dev/tauri/choam/internal/mcas/AbstractHamt.scala @@ -21,11 +21,7 @@ package mcas import scala.util.hashing.MurmurHash3 -private[mcas] abstract class AbstractHamt[K, V, E, T1, T2, H <: AbstractHamt[K, V, E, T1, T2, H]] protected[mcas] () { this: H => - - protected def keyOf(a: V): K - - protected def hashOf(k: K): Long +private[mcas] abstract class AbstractHamt[K <: Hamt.HasHash, V <: Hamt.HasKey[K], E, T1, T2, H <: AbstractHamt[K, V, E, T1, T2, H]] protected[mcas] () { this: H => protected def newArray(size: Int): Array[E] @@ -189,7 +185,7 @@ private[mcas] abstract class AbstractHamt[K, V, E, T1, T2, H <: AbstractHamt[K, case node: AbstractHamt[_, _, _, _, _, _] => curr = node.hashCodeInternal(curr) case a => - curr = MurmurHash3.mix(curr, (hashOf(keyOf(a.asInstanceOf[V])) >>> 32).toInt) + curr = MurmurHash3.mix(curr, (a.asInstanceOf[V].key.hash >>> 32).toInt) curr = MurmurHash3.mix(curr, a.##) } i += 1 diff --git a/mcas/shared/src/main/scala/dev/tauri/choam/internal/mcas/Hamt.scala b/mcas/shared/src/main/scala/dev/tauri/choam/internal/mcas/Hamt.scala index 1379dfa7..85286486 100644 --- a/mcas/shared/src/main/scala/dev/tauri/choam/internal/mcas/Hamt.scala +++ b/mcas/shared/src/main/scala/dev/tauri/choam/internal/mcas/Hamt.scala @@ -68,7 +68,7 @@ import java.util.Arrays * Public methods are the "external" API. We take care never to call them * on a node in lower levels (they assume they're called on the root). */ -private[mcas] abstract class Hamt[K, V, E, T1, T2, H <: Hamt[K, V, E, T1, T2, H]] protected[mcas] ( +private[mcas] abstract class Hamt[K <: Hamt.HasHash, V <: Hamt.HasKey[K], E, T1, T2, H <: Hamt[K, V, E, T1, T2, H]] protected[mcas] ( private val sizeAndBlue: Int, @@ -139,7 +139,7 @@ private[mcas] abstract class Hamt[K, V, E, T1, T2, H <: Hamt[K, V, E, T1, T2, H] /** Must already contain the key of `a` */ final def updated(a: V): H = { - this.insertOrOverwrite(hashOf(keyOf(a)), a, 0, OP_UPDATE) match { + this.insertOrOverwrite(a.key.hash, a, 0, OP_UPDATE) match { case null => this case newRoot => newRoot } @@ -147,7 +147,7 @@ private[mcas] abstract class Hamt[K, V, E, T1, T2, H <: Hamt[K, V, E, T1, T2, H] /** Mustn't already contain the key of `a` */ final def inserted(a: V): H = { - val newRoot = this.insertOrOverwrite(hashOf(keyOf(a)), a, 0, OP_INSERT) + val newRoot = this.insertOrOverwrite(a.key.hash, a, 0, OP_INSERT) assert(newRoot ne null) newRoot } @@ -158,21 +158,21 @@ private[mcas] abstract class Hamt[K, V, E, T1, T2, H <: Hamt[K, V, E, T1, T2, H] /** May or may not already contain the key of `a` */ final def upserted(a: V): H = { - this.insertOrOverwrite(hashOf(keyOf(a)), a, 0, OP_UPSERT) match { + this.insertOrOverwrite(a.key.hash, a, 0, OP_UPSERT) match { case null => this case newRoot => newRoot } } final def computeIfAbsent[T](k: K, tok: T, visitor: Hamt.EntryVisitor[K, V, T]): H = { - this.visit(k, hashOf(k), tok, visitor, modify = false, shift = 0) match { + this.visit(k, k.hash, tok, visitor, modify = false, shift = 0) match { case null => this case newRoot => newRoot } } final def computeOrModify[T](k: K, tok: T, visitor: Hamt.EntryVisitor[K, V, T]): H = { - this.visit(k, hashOf(k), tok, visitor, modify = true, shift = 0) match { + this.visit(k, k.hash, tok, visitor, modify = true, shift = 0) match { case null => this case newRoot => newRoot } @@ -219,7 +219,7 @@ private[mcas] abstract class Hamt[K, V, E, T1, T2, H <: Hamt[K, V, E, T1, T2, H] node.lookupOrNull(hash, shift + W).asInstanceOf[V] case value => val a = value.asInstanceOf[V] - val hashA = hashOf(keyOf(a)) + val hashA = a.key.hash if (hash == hashA) { a } else { @@ -253,7 +253,7 @@ private[mcas] abstract class Hamt[K, V, E, T1, T2, H <: Hamt[K, V, E, T1, T2, H] case null => nullOf[H] case newVal => - assert(hashOf(keyOf(newVal)) == hash) + assert(newVal.key.hash == hash) // TODO: this will compute physIdx again: this.insertOrOverwrite(hash, newVal, shift, op = OP_INSERT) } @@ -272,14 +272,14 @@ private[mcas] abstract class Hamt[K, V, E, T1, T2, H <: Hamt[K, V, E, T1, T2, H] } case value => val a = value.asInstanceOf[V] - val hashA = hashOf(keyOf(a)) + val hashA = a.key.hash if (hash == hashA) { val newEntry = visitor.entryPresent(k, a, tok) if (modify) { if (equ(newEntry, a)) { nullOf[H] } else { - assert(hashOf(keyOf(newEntry)) == hashA) + assert(newEntry.key.hash == hashA) this.insertOrOverwrite(hashA, newEntry, shift, op = OP_UPDATE) } } else { @@ -291,7 +291,7 @@ private[mcas] abstract class Hamt[K, V, E, T1, T2, H <: Hamt[K, V, E, T1, T2, H] case null => nullOf[H] case newVal => - assert(hashOf(keyOf(newVal)) == hash) + assert(newVal.key.hash == hash) // TODO: this will compute physIdx again: this.insertOrOverwrite(hash, newVal, shift, op = OP_INSERT) } @@ -316,7 +316,7 @@ private[mcas] abstract class Hamt[K, V, E, T1, T2, H <: Hamt[K, V, E, T1, T2, H] this.withNode(this.size + (newNode.size - node.size), bitmap, newNode, physIdx) } case ov => - val oh = hashOf(keyOf(ov.asInstanceOf[V])) + val oh = ov.asInstanceOf[V].key.hash if (hash == oh) { if (op == OP_INSERT) { throw new IllegalArgumentException @@ -433,7 +433,15 @@ private[mcas] abstract class Hamt[K, V, E, T1, T2, H <: Hamt[K, V, E, T1, T2, H] private[choam] object Hamt { - trait EntryVisitor[K, V, T] { // TODO: maybe move this to AbstractHamt? + trait HasKey[K <: HasHash] { + def key: K + } + + trait HasHash { + def hash: Long + } + + trait EntryVisitor[K, V, T] { def entryPresent(k: K, v: V, tok: T): V def entryAbsent(k: K, tok: T): V } diff --git a/mcas/shared/src/main/scala/dev/tauri/choam/internal/mcas/MemoryLocation.scala b/mcas/shared/src/main/scala/dev/tauri/choam/internal/mcas/MemoryLocation.scala index b166149b..4ad62087 100644 --- a/mcas/shared/src/main/scala/dev/tauri/choam/internal/mcas/MemoryLocation.scala +++ b/mcas/shared/src/main/scala/dev/tauri/choam/internal/mcas/MemoryLocation.scala @@ -52,7 +52,7 @@ import cats.kernel.Order * directly. Instead, use MCAS, or an even higher * level abstraction. */ -trait MemoryLocation[A] { +trait MemoryLocation[A] extends Hamt.HasHash { // contents: @@ -100,6 +100,9 @@ trait MemoryLocation[A] { def id: Long + final override def hash: Long = + this.id + // private utilities: private[mcas] final def cast[B]: MemoryLocation[B] = diff --git a/mcas/shared/src/main/scala/dev/tauri/choam/internal/mcas/MutHamt.scala b/mcas/shared/src/main/scala/dev/tauri/choam/internal/mcas/MutHamt.scala index cf25b5e3..214bad87 100644 --- a/mcas/shared/src/main/scala/dev/tauri/choam/internal/mcas/MutHamt.scala +++ b/mcas/shared/src/main/scala/dev/tauri/choam/internal/mcas/MutHamt.scala @@ -22,7 +22,7 @@ package mcas /** * Mutable HAMT; not thread safe; `null` values are forbidden. */ -private[mcas] abstract class MutHamt[K, V, E, T1, T2, I <: Hamt[_, _, _, _, _, I], H <: MutHamt[K, V, E, T1, T2, I, H]] protected[mcas] ( +private[mcas] abstract class MutHamt[K <: Hamt.HasHash, V <: Hamt.HasKey[K], E, T1, T2, I <: Hamt[_, _, _, _, _, I], H <: MutHamt[K, V, E, T1, T2, I, H]] protected[mcas] ( // NB: the root doesn't have a logical idx, so we're abusing this field to store the tree size (and also the blue bit) private var logIdx: Int, private var contents: Array[AnyRef], @@ -86,14 +86,14 @@ private[mcas] abstract class MutHamt[K, V, E, T1, T2, I <: Hamt[_, _, _, _, _, I } final def update(a: V): Unit = { - val sdb = this.insertOrOverwrite(hashOf(keyOf(a)), a, shift = 0, op = OP_UPDATE) + val sdb = this.insertOrOverwrite(a.key.hash, a, shift = 0, op = OP_UPDATE) val sizeDiff = unpackSizeDiff(sdb) assert(sizeDiff == 0) this.isBlueTree &= unpackIsBlue(sdb) } final def insert(a: V): Unit = { - val sdb = this.insertOrOverwrite(hashOf(keyOf(a)), a, shift = 0, op = OP_INSERT) + val sdb = this.insertOrOverwrite(a.key.hash, a, shift = 0, op = OP_INSERT) val sizeDiff = unpackSizeDiff(sdb) assert(sizeDiff == 1) this.addToSize(1) @@ -105,7 +105,7 @@ private[mcas] abstract class MutHamt[K, V, E, T1, T2, I <: Hamt[_, _, _, _, _, I } final def upsert(a: V): Unit = { - val sdb = this.insertOrOverwrite(hashOf(keyOf(a)), a, shift = 0, op = OP_UPSERT) + val sdb = this.insertOrOverwrite(a.key.hash, a, shift = 0, op = OP_UPSERT) val sizeDiff = unpackSizeDiff(sdb) assert((sizeDiff == 0) || (sizeDiff == 1)) this.addToSize(sizeDiff) @@ -113,7 +113,7 @@ private[mcas] abstract class MutHamt[K, V, E, T1, T2, I <: Hamt[_, _, _, _, _, I } final def computeIfAbsent[T](k: K, tok: T, visitor: Hamt.EntryVisitor[K, V, T]): Unit = { - val sdb = this.visit(k, hashOf(k), tok, visitor, newValue = nullOf[V], modify = false, shift = 0) + val sdb = this.visit(k, k.hash, tok, visitor, newValue = nullOf[V], modify = false, shift = 0) val sizeDiff = unpackSizeDiff(sdb) assert((sizeDiff == 0) || (sizeDiff == 1)) this.addToSize(sizeDiff) @@ -121,7 +121,7 @@ private[mcas] abstract class MutHamt[K, V, E, T1, T2, I <: Hamt[_, _, _, _, _, I } final def computeOrModify[T](k: K, tok: T, visitor: Hamt.EntryVisitor[K, V, T]): Unit = { - val sdb = this.visit(k, hashOf(k), tok, visitor, newValue = nullOf[V], modify = true, shift = 0) + val sdb = this.visit(k, k.hash, tok, visitor, newValue = nullOf[V], modify = true, shift = 0) val sizeDiff = unpackSizeDiff(sdb) assert((sizeDiff == 0) || (sizeDiff == 1)) this.addToSize(sizeDiff) @@ -166,7 +166,7 @@ private[mcas] abstract class MutHamt[K, V, E, T1, T2, I <: Hamt[_, _, _, _, _, I node.lookupOrNull(hash, shift + W).asInstanceOf[V] case value => val a = value.asInstanceOf[V] - val hashA = hashOf(keyOf(a)) + val hashA = a.key.hash if (hash == hashA) { a } else { @@ -204,7 +204,7 @@ private[mcas] abstract class MutHamt[K, V, E, T1, T2, I <: Hamt[_, _, _, _, _, I case null => packSizeDiffAndBlue(0, true) case newVal => - assert(hashOf(keyOf(newVal)) == hash) + assert(newVal.key.hash == hash) // TODO: this will compute physIdx again: this.insertOrOverwrite(hash, newVal, shift, op = OP_INSERT) } @@ -231,7 +231,7 @@ private[mcas] abstract class MutHamt[K, V, E, T1, T2, I <: Hamt[_, _, _, _, _, I } case value => val a = value.asInstanceOf[V] - val hashA = hashOf(keyOf(a)) + val hashA = a.key.hash if (hash == hashA) { val newVal = if (isNull(newValue)) { visitor.entryPresent(k, a, tok) @@ -242,7 +242,7 @@ private[mcas] abstract class MutHamt[K, V, E, T1, T2, I <: Hamt[_, _, _, _, _, I if (equ(newValue, a)) { packSizeDiffAndBlue(0, isBlue(a)) } else { - assert(hashOf(keyOf(newVal)) == hashA) + assert(newVal.key.hash == hashA) this.insertOrOverwrite(hash, newVal, shift, op = OP_UPDATE) } } else { @@ -263,7 +263,7 @@ private[mcas] abstract class MutHamt[K, V, E, T1, T2, I <: Hamt[_, _, _, _, _, I case null => packSizeDiffAndBlue(0, true) case newVal => - assert(hashOf(keyOf(newVal)) == hash) + assert(newVal.key.hash == hash) this.insertOrOverwrite(hash, newVal, shift, op = OP_INSERT) } } else { @@ -316,7 +316,7 @@ private[mcas] abstract class MutHamt[K, V, E, T1, T2, I <: Hamt[_, _, _, _, _, I } } case ov => - val oh = hashOf(keyOf(ov.asInstanceOf[V])) + val oh = ov.asInstanceOf[V].key.hash if (hash == oh) { if (op == OP_INSERT) { throw new IllegalArgumentException @@ -373,7 +373,7 @@ private[mcas] abstract class MutHamt[K, V, E, T1, T2, I <: Hamt[_, _, _, _, _, I val newPhysIdx = physicalIdx(logIdx, newSize, shift = shift) newContents(newPhysIdx) = node case value => - val logIdx = logicalIdx(hashOf(keyOf(value.asInstanceOf[V])), shift) + val logIdx = logicalIdx(value.asInstanceOf[V].key.hash, shift) val newPhysIdx = physicalIdx(logIdx, newSize, shift = shift) newContents(newPhysIdx) = value } @@ -412,7 +412,7 @@ private[mcas] abstract class MutHamt[K, V, E, T1, T2, I <: Hamt[_, _, _, _, _, I arr(arity) = box(child) arity += 1 case value => - bitmap |= (1L << logicalIdx(hashOf(keyOf(value.asInstanceOf[V])), shift = shift)) + bitmap |= (1L << logicalIdx(value.asInstanceOf[V].key.hash, shift = shift)) size += 1 isBlueSubtree &= isBlue(value.asInstanceOf[V]) arr(arity) = value diff --git a/mcas/shared/src/test/scala/dev/tauri/choam/internal/mcas/HamtSpec.scala b/mcas/shared/src/test/scala/dev/tauri/choam/internal/mcas/HamtSpec.scala index 667a0055..f0417396 100644 --- a/mcas/shared/src/test/scala/dev/tauri/choam/internal/mcas/HamtSpec.scala +++ b/mcas/shared/src/test/scala/dev/tauri/choam/internal/mcas/HamtSpec.scala @@ -33,7 +33,7 @@ import org.scalacheck.Prop.forAll final class HamtSpec extends ScalaCheckSuite with MUnitUtils with PropertyHelpers { - import HamtSpec.{ LongHamt, Val, SpecVal, hamtFromList, addAll } + import HamtSpec.{ LongHamt, LongWr, Val, SpecVal, hamtFromList, addAll } override protected def scalaCheckTestParameters: org.scalacheck.Test.Parameters = { val p = super.scalaCheckTestParameters @@ -227,31 +227,31 @@ final class HamtSpec extends ScalaCheckSuite with MUnitUtils with PropertyHelper for (n <- nums) { val v = Val(n) var count = 0 - val nullVis = new Hamt.EntryVisitor[Long, Val, AnyRef] { - override def entryPresent(k: Long, a: Val, tok: AnyRef): Val = + val nullVis = new Hamt.EntryVisitor[LongWr, Val, AnyRef] { + override def entryPresent(k: LongWr, a: Val, tok: AnyRef): Val = fail("present called") - override def entryAbsent(k: Long, tok: AnyRef): Val = { - assertEquals(k, n) + override def entryAbsent(k: LongWr, tok: AnyRef): Val = { + assertEquals(k.n, n) assertSameInstance(tok, token1) count += 1 null } } - val newHamt = hamt.computeIfAbsent(n, token1, nullVis) + val newHamt = hamt.computeIfAbsent(LongWr(n), token1, nullVis) assertEquals(count, 1) assertSameInstance(newHamt, hamt) val token2 = new AnyRef - val vis = new Hamt.EntryVisitor[Long, Val, AnyRef] { - override def entryPresent(k: Long, a: Val, tok: AnyRef): Val = + val vis = new Hamt.EntryVisitor[LongWr, Val, AnyRef] { + override def entryPresent(k: LongWr, a: Val, tok: AnyRef): Val = fail("present called") - override def entryAbsent(k: Long, tok: AnyRef): Val = { - assertEquals(k, n) + override def entryAbsent(k: LongWr, tok: AnyRef): Val = { + assertEquals(k.n, n) assertSameInstance(tok, token2) count += 1 v } } - hamt = hamt.computeIfAbsent(n, token2, vis) + hamt = hamt.computeIfAbsent(LongWr(n), token2, vis) assertEquals(count, 2) assertEquals(hamt.getOrElse(n, null), v) } @@ -259,32 +259,32 @@ final class HamtSpec extends ScalaCheckSuite with MUnitUtils with PropertyHelper var e: Val = null var count = 0 val token3 = new AnyRef - val incorrectVisitor = new Hamt.EntryVisitor[Long, Val, AnyRef] { - override def entryPresent(k: Long, a: Val, tok: AnyRef): Val = { - assertEquals(k, n) + val incorrectVisitor = new Hamt.EntryVisitor[LongWr, Val, AnyRef] { + override def entryPresent(k: LongWr, a: Val, tok: AnyRef): Val = { + assertEquals(k.n, n) assertSameInstance(tok, token3) count += 1 e = a new Val(a.value) // same value, different instance } - override def entryAbsent(k: Long, tok: AnyRef): Val = + override def entryAbsent(k: LongWr, tok: AnyRef): Val = fail("absent called") } assert(Either.catchOnly[AssertionError] { - hamt.computeIfAbsent(n, token3, incorrectVisitor) + hamt.computeIfAbsent(LongWr(n), token3, incorrectVisitor) }.isLeft) - val vis = new Hamt.EntryVisitor[Long, Val, AnyRef] { - override def entryPresent(k: Long, a: Val, tok: AnyRef): Val = { - assertEquals(k, n) + val vis = new Hamt.EntryVisitor[LongWr, Val, AnyRef] { + override def entryPresent(k: LongWr, a: Val, tok: AnyRef): Val = { + assertEquals(k.n, n) assertSameInstance(tok, token3) count += 1 e = a a } - override def entryAbsent(k: Long, tok: AnyRef): Val = + override def entryAbsent(k: LongWr, tok: AnyRef): Val = fail("absent called") } - val newHamt = hamt.computeIfAbsent(n, token3, vis) + val newHamt = hamt.computeIfAbsent(LongWr(n), token3, vis) assertEquals(count, 2) assertEquals(e, Val(n)) assertSameInstance(newHamt, hamt) @@ -299,31 +299,31 @@ final class HamtSpec extends ScalaCheckSuite with MUnitUtils with PropertyHelper for (n <- nums) { val v = Val(n) var count = 0 - val nullVis = new Hamt.EntryVisitor[Long, Val, AnyRef] { - override def entryPresent(k: Long, a: Val, tok: AnyRef): Val = + val nullVis = new Hamt.EntryVisitor[LongWr, Val, AnyRef] { + override def entryPresent(k: LongWr, a: Val, tok: AnyRef): Val = fail("present called") - override def entryAbsent(k: Long, tok: AnyRef): Val = { - assertEquals(k, n) + override def entryAbsent(k: LongWr, tok: AnyRef): Val = { + assertEquals(k.n, n) assertSameInstance(tok, token1) count += 1 null } } - val newHamt = hamt.computeOrModify(n, token1, nullVis) + val newHamt = hamt.computeOrModify(LongWr(n), token1, nullVis) assertEquals(count, 1) assertSameInstance(newHamt, hamt) val token2 = new AnyRef - val vis = new Hamt.EntryVisitor[Long, Val, AnyRef] { - override def entryPresent(k: Long, a: Val, tok: AnyRef): Val = + val vis = new Hamt.EntryVisitor[LongWr, Val, AnyRef] { + override def entryPresent(k: LongWr, a: Val, tok: AnyRef): Val = fail("present called") - override def entryAbsent(k: Long, tok: AnyRef): Val = { - assertEquals(k, n) + override def entryAbsent(k: LongWr, tok: AnyRef): Val = { + assertEquals(k.n, n) assertSameInstance(tok, token2) count += 1 v } } - hamt = hamt.computeOrModify(n, token2, vis) + hamt = hamt.computeOrModify(LongWr(n), token2, vis) assertEquals(count, 2) assertEquals(hamt.getOrElse(n, null), v) } @@ -331,32 +331,32 @@ final class HamtSpec extends ScalaCheckSuite with MUnitUtils with PropertyHelper var e: Val = null var count = 0 val token3 = new AnyRef - val readOnlyVisitor = new Hamt.EntryVisitor[Long, Val, AnyRef] { - override def entryPresent(k: Long, a: Val, tok: AnyRef): Val = { - assertEquals(k, n) + val readOnlyVisitor = new Hamt.EntryVisitor[LongWr, Val, AnyRef] { + override def entryPresent(k: LongWr, a: Val, tok: AnyRef): Val = { + assertEquals(k.n, n) assertSameInstance(tok, token3) count += 1 e = a a } - override def entryAbsent(k: Long, tok: AnyRef): Val = + override def entryAbsent(k: LongWr, tok: AnyRef): Val = fail("absent called") } - val newHamt2 = hamt.computeOrModify(n, token3, readOnlyVisitor) + val newHamt2 = hamt.computeOrModify(LongWr(n), token3, readOnlyVisitor) assertSameInstance(newHamt2, hamt) - val vis = new Hamt.EntryVisitor[Long, Val, AnyRef] { - override def entryPresent(k: Long, a: Val, tok: AnyRef): Val = { - assertEquals(k, n) + val vis = new Hamt.EntryVisitor[LongWr, Val, AnyRef] { + override def entryPresent(k: LongWr, a: Val, tok: AnyRef): Val = { + assertEquals(k.n, n) assertSameInstance(tok, token3) count += 1 val newA = Val(a.value, extra = "foo") e = newA newA } - override def entryAbsent(k: Long, tok: AnyRef): Val = + override def entryAbsent(k: LongWr, tok: AnyRef): Val = fail("absent called") } - val newHamt = hamt.computeOrModify(n, token3, vis) + val newHamt = hamt.computeOrModify(LongWr(n), token3, vis) assertEquals(count, 2) assertEquals(e, Val(n, "foo")) assert(newHamt ne hamt) @@ -573,8 +573,16 @@ final class HamtSpec extends ScalaCheckSuite with MUnitUtils with PropertyHelper object HamtSpec { - /** Just a `Long`, but has its own identity */ - case class Val(value: Long, extra: String = "fortytwo", isBlue: Boolean = true) { + case class LongWr(n: Long) extends Hamt.HasHash { + final override def hash: Long = + n + } + + case class Val(value: Long, extra: String = "fortytwo", isBlue: Boolean = true) extends Hamt.HasKey[LongWr] { + + final override val key: LongWr = + LongWr(value) + override def equals(that: Any): Boolean = { if (that.isInstanceOf[SpecVal]) { that.equals(this) @@ -600,11 +608,7 @@ object HamtSpec { _sizeAndBlue: Int, _bitmap: Long, _contents: Array[AnyRef], - ) extends Hamt[Long, Val, Val, Unit, Long, LongHamt](_sizeAndBlue, _bitmap, _contents) { - protected final override def hashOf(k: Long): Long = - k - protected final override def keyOf(a: Val): Long = - a.value + ) extends Hamt[LongWr, Val, Val, Unit, Long, LongHamt](_sizeAndBlue, _bitmap, _contents) { protected final override def isBlue(a: Val): Boolean = a.isBlue protected final override def newNode(size: Int, bitmap: Long, contents: Array[AnyRef]): LongHamt = diff --git a/mcas/shared/src/test/scala/dev/tauri/choam/internal/mcas/MutHamtSpec.scala b/mcas/shared/src/test/scala/dev/tauri/choam/internal/mcas/MutHamtSpec.scala index dd7fb68e..8641e23f 100644 --- a/mcas/shared/src/test/scala/dev/tauri/choam/internal/mcas/MutHamtSpec.scala +++ b/mcas/shared/src/test/scala/dev/tauri/choam/internal/mcas/MutHamtSpec.scala @@ -33,7 +33,7 @@ import org.scalacheck.Prop.forAll final class MutHamtSpec extends ScalaCheckSuite with MUnitUtils with PropertyHelpers { - import HamtSpec.{ Val, SpecVal, hamtFromList, addAll } + import HamtSpec.{ Val, SpecVal, LongWr, hamtFromList, addAll } import MutHamtSpec.LongMutHamt override protected def scalaCheckTestParameters: org.scalacheck.Test.Parameters = { @@ -292,11 +292,11 @@ final class MutHamtSpec extends ScalaCheckSuite with MUnitUtils with PropertyHel for (n <- nums) { val v = Val(n) var count = 0 - val nullVis = new Hamt.EntryVisitor[Long, Val, AnyRef] { - override def entryPresent(k: Long, a: Val, tok: AnyRef): Val = + val nullVis = new Hamt.EntryVisitor[LongWr, Val, AnyRef] { + override def entryPresent(k: LongWr, a: Val, tok: AnyRef): Val = fail("present called") - override def entryAbsent(k: Long, tok: AnyRef): Val = { - assertEquals(k, n) + override def entryAbsent(k: LongWr, tok: AnyRef): Val = { + assertEquals(k.n, n) assertSameInstance(tok, token1) count += 1 null @@ -304,23 +304,23 @@ final class MutHamtSpec extends ScalaCheckSuite with MUnitUtils with PropertyHel } val oldSize = hamt.size val oldValues = hamt.toArray.toList - hamt.computeIfAbsent(n, token1, nullVis) + hamt.computeIfAbsent(LongWr(n), token1, nullVis) assertEquals(count, 1) assertEquals(hamt.size, oldSize) assertEquals(hamt.toArray.toList, oldValues) val immutable = hamtFromList(oldValues.map(_.value)) val token2 = new AnyRef - val vis = new Hamt.EntryVisitor[Long, Val, AnyRef] { - override def entryPresent(k: Long, a: Val, tok: AnyRef): Val = + val vis = new Hamt.EntryVisitor[LongWr, Val, AnyRef] { + override def entryPresent(k: LongWr, a: Val, tok: AnyRef): Val = fail("present called") - override def entryAbsent(k: Long, tok: AnyRef): Val = { - assertEquals(k, n) + override def entryAbsent(k: LongWr, tok: AnyRef): Val = { + assertEquals(k.n, n) assertSameInstance(tok, token2) count += 1 v } } - hamt.computeIfAbsent(n, token2, vis) + hamt.computeIfAbsent(LongWr(n), token2, vis) assertEquals(count, 2) assertEquals(hamt.getOrElse(n, null), v) assertEquals(hamt.size, oldSize + 1) @@ -330,36 +330,36 @@ final class MutHamtSpec extends ScalaCheckSuite with MUnitUtils with PropertyHel var e: Val = null var count = 0 val token3 = new AnyRef - val incorrectVisitor = new Hamt.EntryVisitor[Long, Val, AnyRef] { - override def entryPresent(k: Long, a: Val, tok: AnyRef): Val = { - assertEquals(k, n) + val incorrectVisitor = new Hamt.EntryVisitor[LongWr, Val, AnyRef] { + override def entryPresent(k: LongWr, a: Val, tok: AnyRef): Val = { + assertEquals(k.n, n) assertSameInstance(tok, token3) count += 1 e = a new Val(a.value) // same value, different instance } - override def entryAbsent(k: Long, tok: AnyRef): Val = + override def entryAbsent(k: LongWr, tok: AnyRef): Val = fail("absent called") } val oldSize = hamt.size val oldValues = hamt.toArray.toList assert(Either.catchOnly[AssertionError] { - hamt.computeIfAbsent(n, token3, incorrectVisitor) + hamt.computeIfAbsent(LongWr(n), token3, incorrectVisitor) }.isLeft) assertEquals(hamt.size, oldSize) assertEquals(hamt.toArray.toList, oldValues) - val vis = new Hamt.EntryVisitor[Long, Val, AnyRef] { - override def entryPresent(k: Long, a: Val, tok: AnyRef): Val = { - assertEquals(k, n) + val vis = new Hamt.EntryVisitor[LongWr, Val, AnyRef] { + override def entryPresent(k: LongWr, a: Val, tok: AnyRef): Val = { + assertEquals(k.n, n) assertSameInstance(tok, token3) count += 1 e = a a } - override def entryAbsent(k: Long, tok: AnyRef): Val = + override def entryAbsent(k: LongWr, tok: AnyRef): Val = fail("absent called") } - hamt.computeIfAbsent(n, token3, vis) + hamt.computeIfAbsent(LongWr(n), token3, vis) assertEquals(count, 2) assertEquals(e, Val(n)) assertEquals(hamt.size, oldSize) @@ -375,11 +375,11 @@ final class MutHamtSpec extends ScalaCheckSuite with MUnitUtils with PropertyHel for (n <- nums) { val v = Val(n) var count = 0 - val nullVis = new Hamt.EntryVisitor[Long, Val, AnyRef] { - override def entryPresent(k: Long, a: Val, tok: AnyRef): Val = + val nullVis = new Hamt.EntryVisitor[LongWr, Val, AnyRef] { + override def entryPresent(k: LongWr, a: Val, tok: AnyRef): Val = fail("present called") - override def entryAbsent(k: Long, tok: AnyRef): Val = { - assertEquals(k, n) + override def entryAbsent(k: LongWr, tok: AnyRef): Val = { + assertEquals(k.n, n) assertSameInstance(tok, token1) count += 1 null @@ -388,22 +388,22 @@ final class MutHamtSpec extends ScalaCheckSuite with MUnitUtils with PropertyHel val oldSize = hamt.size val oldValues = hamt.toArray.toList val immutable = hamtFromList(oldValues.map(_.value)) - hamt.computeOrModify(n, token1, nullVis) + hamt.computeOrModify(LongWr(n), token1, nullVis) assertEquals(count, 1) assertEquals(hamt.size, oldSize) assertEquals(hamt.toArray.toList, oldValues) val token2 = new AnyRef - val vis = new Hamt.EntryVisitor[Long, Val, AnyRef] { - override def entryPresent(k: Long, a: Val, tok: AnyRef): Val = + val vis = new Hamt.EntryVisitor[LongWr, Val, AnyRef] { + override def entryPresent(k: LongWr, a: Val, tok: AnyRef): Val = fail("present called") - override def entryAbsent(k: Long, tok: AnyRef): Val = { - assertEquals(k, n) + override def entryAbsent(k: LongWr, tok: AnyRef): Val = { + assertEquals(k.n, n) assertSameInstance(tok, token2) count += 1 v } } - hamt.computeOrModify(n, token2, vis) + hamt.computeOrModify(LongWr(n), token2, vis) assertEquals(count, 2) assertEquals(hamt.getOrElse(n, null), v) assertEquals(hamt.size, oldSize + 1) @@ -413,35 +413,35 @@ final class MutHamtSpec extends ScalaCheckSuite with MUnitUtils with PropertyHel var e: Val = null var count = 0 val token3 = new AnyRef - val readOnlyVisitor = new Hamt.EntryVisitor[Long, Val, AnyRef] { - override def entryPresent(k: Long, a: Val, tok: AnyRef): Val = { - assertEquals(k, n) + val readOnlyVisitor = new Hamt.EntryVisitor[LongWr, Val, AnyRef] { + override def entryPresent(k: LongWr, a: Val, tok: AnyRef): Val = { + assertEquals(k.n, n) assertSameInstance(tok, token3) count += 1 e = a a } - override def entryAbsent(k: Long, tok: AnyRef): Val = + override def entryAbsent(k: LongWr, tok: AnyRef): Val = fail("absent called") } val oldSize = hamt.size val oldValues = hamt.toArray.toList - hamt.computeOrModify(n, token3, readOnlyVisitor) + hamt.computeOrModify(LongWr(n), token3, readOnlyVisitor) assertEquals(hamt.size, oldSize) assertEquals(hamt.toArray.toList, oldValues) - val vis = new Hamt.EntryVisitor[Long, Val, AnyRef] { - override def entryPresent(k: Long, a: Val, tok: AnyRef): Val = { - assertEquals(k, n) + val vis = new Hamt.EntryVisitor[LongWr, Val, AnyRef] { + override def entryPresent(k: LongWr, a: Val, tok: AnyRef): Val = { + assertEquals(k.n, n) assertSameInstance(tok, token3) count += 1 val newA = Val(a.value, extra = "foo") e = newA newA } - override def entryAbsent(k: Long, tok: AnyRef): Val = + override def entryAbsent(k: LongWr, tok: AnyRef): Val = fail("absent called") } - hamt.computeOrModify(n, token3, vis) + hamt.computeOrModify(LongWr(n), token3, vis) assertEquals(count, 2) assertEquals(e, Val(n, "foo")) assertEquals(hamt.getOrElse(n, Val(42L)), Val(n, "foo")) @@ -702,18 +702,12 @@ final class MutHamtSpec extends ScalaCheckSuite with MUnitUtils with PropertyHel object MutHamtSpec { - import HamtSpec.{ Val, LongHamt } + import HamtSpec.{ Val, LongWr, LongHamt } final class LongMutHamt( logIdx: Int, contents: Array[AnyRef], - ) extends MutHamt[Long, Val, Val, Unit, Long, LongHamt, LongMutHamt](logIdx, contents) { - - protected final override def keyOf(a: Val): Long = - a.value - - protected final override def hashOf(k: Long): Long = - k + ) extends MutHamt[LongWr, Val, Val, Unit, Long, LongHamt, LongMutHamt](logIdx, contents) { protected final override def isBlue(a: Val): Boolean = a.isBlue