diff options
author | Lukas Rytz <lukas.rytz@typesafe.com> | 2016-05-25 06:30:12 +0200 |
---|---|---|
committer | Lukas Rytz <lukas.rytz@typesafe.com> | 2016-05-25 06:30:12 +0200 |
commit | c17ef7758bee4be23dcd91701d54f09220bdc343 (patch) | |
tree | 710fe9bc16d203f3ef162b4fa0da50b614527650 /src | |
parent | 808f3d071e97aa23b797f2c0616c207ff1f20229 (diff) | |
parent | 60f28f9e6a330e91a0f1204917301d401a6fce72 (diff) | |
download | scala-c17ef7758bee4be23dcd91701d54f09220bdc343.tar.gz scala-c17ef7758bee4be23dcd91701d54f09220bdc343.tar.bz2 scala-c17ef7758bee4be23dcd91701d54f09220bdc343.zip |
Merge pull request #5124 from performantdata/bug/SI-9522
SI-9522 release key reference when deleting from OpenHashMap
Diffstat (limited to 'src')
-rw-r--r-- | src/eclipse/repl/.classpath | 2 | ||||
-rw-r--r-- | src/eclipse/test-junit/.classpath | 1 | ||||
-rw-r--r-- | src/library/scala/collection/mutable/OpenHashMap.scala | 83 |
3 files changed, 56 insertions, 30 deletions
diff --git a/src/eclipse/repl/.classpath b/src/eclipse/repl/.classpath index 682377adc9..141f84e6bb 100644 --- a/src/eclipse/repl/.classpath +++ b/src/eclipse/repl/.classpath @@ -2,7 +2,7 @@ <classpath> <classpathentry kind="src" path="repl"/> <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/asm/scala-asm-5.0.4-scala-3.jar"/> - <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/repl/jline-2.12.1.jar"/> + <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/repl/jline-2.14.1.jar"/> <classpathentry combineaccessrules="false" kind="src" path="/scala-compiler"/> <classpathentry combineaccessrules="false" kind="src" path="/scala-library"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> diff --git a/src/eclipse/test-junit/.classpath b/src/eclipse/test-junit/.classpath index 3635c85112..1e1b510663 100644 --- a/src/eclipse/test-junit/.classpath +++ b/src/eclipse/test-junit/.classpath @@ -11,6 +11,7 @@ <classpathentry combineaccessrules="false" kind="src" path="/partest-extras"/> <classpathentry combineaccessrules="false" kind="src" path="/scaladoc"/> <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/scaladoc/scala-xml_2.12.0-M4-1.0.5.jar"/> + <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/junit/jol-core-0.5.jar"/> <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/> <classpathentry kind="output" path="build-test-junit"/> </classpath> diff --git a/src/library/scala/collection/mutable/OpenHashMap.scala b/src/library/scala/collection/mutable/OpenHashMap.scala index 5f8f5b9a0a..5bea1634c4 100644 --- a/src/library/scala/collection/mutable/OpenHashMap.scala +++ b/src/library/scala/collection/mutable/OpenHashMap.scala @@ -21,10 +21,16 @@ object OpenHashMap { def apply[K, V](elems : (K, V)*) = new OpenHashMap[K, V] ++= elems def empty[K, V] = new OpenHashMap[K, V] - final private class OpenEntry[Key, Value](val key: Key, - val hash: Int, + /** A hash table entry. + * + * The entry is occupied if and only if its `value` is a `Some`; + * deleted if and only if its `value` is `None`. + * If its `key` is not the default value of type `Key`, the entry is occupied. + * If the entry is occupied, `hash` contains the hash value of `key`. + */ + final private class OpenEntry[Key, Value](var key: Key, + var hash: Int, var value: Option[Value]) - extends HashEntry[Key, OpenEntry[Key, Value]] private[mutable] def nextPositivePowerOfTwo(i : Int) = 1 << (32 - Integer.numberOfLeadingZeros(i - 1)) } @@ -64,7 +70,14 @@ extends AbstractMap[Key, Value] private[this] val actualInitialSize = OpenHashMap.nextPositivePowerOfTwo(initialSize) private var mask = actualInitialSize - 1 - private var table : Array[Entry] = new Array[Entry](actualInitialSize) + + /** The hash table. + * + * The table's entries are initialized to `null`, indication of an empty slot. + * A slot is either deleted or occupied if and only if the entry is non-`null`. + */ + private[this] var table = new Array[Entry](actualInitialSize) + private var _size = 0 private var deleted = 0 @@ -91,42 +104,43 @@ extends AbstractMap[Key, Value] table = new Array[Entry](newSize) mask = newSize - 1 oldTable.foreach( entry => - if (entry != null && entry.value != None) addEntry(entry)) + if (entry != null && entry.value != None) + table(findIndex(entry.key, entry.hash)) = entry ) deleted = 0 } /** Return the index of the first slot in the hash table (in probe order) - * that either is empty, or is or was last occupied by the given key. - */ - private[this] def findIndex(key: Key) : Int = findIndex(key, hashOf(key)) - - /** Return the index of the first slot in the hash table (in probe order) - * that either is empty, or is or was last occupied by the given key. - * - * This method is an optimization for when the hash value is in hand. + * that is, in order of preference, either occupied by the given key, deleted, or empty. * * @param hash hash value for `key` */ private[this] def findIndex(key: Key, hash: Int): Int = { var j = hash - var index = hash & mask var perturb = index - while(table(index) != null && - !(table(index).hash == hash && - table(index).key == key)){ + + /** Index of the first slot containing a deleted entry, or -1 if none found yet. */ + var firstDeletedIndex = -1 + + var entry = table(index) + while (entry != null) { + if (entry.hash == hash && entry.key == key && entry.value != None) + return index + + if (firstDeletedIndex == -1 && entry.value == None) + firstDeletedIndex = index + j = 5 * j + 1 + perturb perturb >>= 5 index = j & mask + entry = table(index) } - index - } - private[this] def addEntry(entry: Entry) = - if (entry != null) table(findIndex(entry.key, entry.hash)) = entry + if (firstDeletedIndex == -1) index else firstDeletedIndex + } override def update(key: Key, value: Value) { - put(key, hashOf(key), value) + put(key, value) } @deprecatedOverriding("+= should not be overridden in order to maintain consistency with put.", "2.11.0") @@ -150,6 +164,8 @@ extends AbstractMap[Key, Value] } else { val res = entry.value if (entry.value == None) { + entry.key = key + entry.hash = hash size += 1 deleted -= 1 modCount += 1 @@ -159,13 +175,22 @@ extends AbstractMap[Key, Value] } } + /** Delete the hash table slot contained in the given entry. */ + @inline + private[this] def deleteSlot(entry: Entry) = { + entry.key = null.asInstanceOf[Key] + entry.hash = 0 + entry.value = None + + size -= 1 + deleted += 1 + } + override def remove(key : Key): Option[Value] = { - val index = findIndex(key) - if (table(index) != null && table(index).value != None){ - val res = table(index).value - table(index).value = None - size -= 1 - deleted += 1 + val entry = table(findIndex(key, hashOf(key))) + if (entry != null && entry.value != None) { + val res = entry.value + deleteSlot(entry) res } else None } @@ -249,7 +274,7 @@ extends AbstractMap[Key, Value] } override def retain(f : (Key, Value) => Boolean) = { - foreachUndeletedEntry(entry => if (!f(entry.key, entry.value.get)) {entry.value = None; size -= 1; deleted += 1} ) + foreachUndeletedEntry(entry => if (!f(entry.key, entry.value.get)) deleteSlot(entry)) this } |