Skip to content

Commit 79edb79

Browse files
committed
Make util.Set and util.HashSet more usable
Add size and -= methods. Rename addEntry to +=. Rename findEntry to lookup. # Conflicts: # compiler/src/dotty/tools/dotc/util/HashSet.scala
1 parent 50a0ef5 commit 79edb79

File tree

6 files changed

+92
-54
lines changed

6 files changed

+92
-54
lines changed

compiler/src/dotty/tools/dotc/core/TypeOps.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ object TypeOps:
187187
/** a faster version of cs1 intersect cs2 */
188188
def intersect(cs1: List[ClassSymbol], cs2: List[ClassSymbol]): List[ClassSymbol] = {
189189
val cs2AsSet = new util.HashSet[ClassSymbol](128)
190-
cs2.foreach(cs2AsSet.addEntry)
190+
cs2.foreach(cs2AsSet +=)
191191
cs1.filter(cs2AsSet.contains)
192192
}
193193

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5540,7 +5540,7 @@ object Types {
55405540
def apply(x: mutable.Set[NamedType], tp: Type): mutable.Set[NamedType] =
55415541
if (seen contains tp) x
55425542
else {
5543-
seen.addEntry(tp)
5543+
seen += tp
55445544
tp match {
55455545
case tp: TypeRef =>
55465546
foldOver(maybeAdd(x, tp), tp)

compiler/src/dotty/tools/dotc/typer/Implicits.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,7 @@ trait ImplicitRunInfo:
534534
if partSeen.contains(t) then ()
535535
else if implicitScopeCache.contains(t) then parts += t
536536
else
537-
partSeen.addEntry(t)
537+
partSeen += t
538538
t.dealias match
539539
case t: TypeRef =>
540540
if isAnchor(t.symbol) then
@@ -578,12 +578,12 @@ trait ImplicitRunInfo:
578578
is.companionRefs
579579
case None =>
580580
if seen.contains(t) then
581-
incomplete.addEntry(tp) // all references for `t` will be accounted for in `seen` so we return `EmptySet`.
581+
incomplete += tp // all references for `t` will be accounted for in `seen` so we return `EmptySet`.
582582
TermRefSet.empty // on the other hand, the refs of `tp` are now inaccurate, so `tp` is marked incomplete.
583583
else
584-
seen.addEntry(t)
584+
seen += t
585585
val is = recur(t)
586-
if !implicitScopeCache.contains(t) then incomplete.addEntry(tp)
586+
if !implicitScopeCache.contains(t) then incomplete += tp
587587
is.companionRefs
588588
end iscopeRefs
589589

@@ -693,7 +693,7 @@ trait ImplicitRunInfo:
693693
if seen.contains(t) then
694694
WildcardType
695695
else
696-
seen.addEntry(t)
696+
seen += t
697697
t.underlying match
698698
case TypeBounds(lo, hi) =>
699699
if defn.isBottomTypeAfterErasure(lo) then apply(hi)

compiler/src/dotty/tools/dotc/typer/RefChecks.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -651,11 +651,11 @@ object RefChecks {
651651
val seenClasses = new util.HashSet[Symbol](256)
652652
def addDecls(cls: Symbol): Unit =
653653
if (!seenClasses.contains(cls)) {
654-
seenClasses.addEntry(cls)
654+
seenClasses += cls
655655
for (mbr <- cls.info.decls)
656656
if (mbr.isTerm && !mbr.isOneOf(Synthetic | Bridge) && mbr.memberCanMatchInheritedSymbols &&
657657
!membersToCheck.contains(mbr.name))
658-
membersToCheck.addEntry(mbr.name)
658+
membersToCheck += mbr.name
659659
cls.info.parents.map(_.classSymbol)
660660
.filter(_.isOneOf(AbstractOrTrait))
661661
.dropWhile(_.isOneOf(JavaDefined | Scala2x))

compiler/src/dotty/tools/dotc/util/HashSet.scala

Lines changed: 74 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,29 @@
11
package dotty.tools.dotc.util
22

33
/** A hash set that allows some privileged protected access to its internals
4+
* @param initialCapacity Indicates the initial number of slots in the hash table.
5+
* The actual number of slots is always a power of 2, so the
6+
* initial size of the table will be the smallest power of two
7+
* that is equal or greater than the given `initialCapacity`.
8+
* @param loadFactor The maximum fraction of used elements relative to capacity.
9+
* The hash table will be re-sized once the number of elements exceeds
10+
* the current size of the hash table multiplied by loadFactor.
11+
* With the defaults given, the first resize of the table happens once the number of elements
12+
* grows beyond 16.
413
*/
5-
class HashSet[T >: Null <: AnyRef](powerOfTwoInitialCapacity: Int = 16, loadFactor: Float = 0.25f) extends MutableSet[T] {
14+
class HashSet[T >: Null <: AnyRef](initialCapacity: Int = 16, loadFactor: Float = 0.25f) extends MutableSet[T] {
615
private var used: Int = _
716
private var limit: Int = _
817
private var table: Array[AnyRef] = _
918

10-
assert(Integer.bitCount(powerOfTwoInitialCapacity) == 1)
19+
private def roundToPower(n: Int) =
20+
if Integer.bitCount(n) == 1 then n
21+
else
22+
def recur(n: Int): Int =
23+
if n == 1 then 2
24+
else recur(n >>> 1) << 1
25+
recur(n)
26+
1127
protected def isEqual(x: T, y: T): Boolean = x.equals(y)
1228

1329
// Counters for Stats
@@ -27,7 +43,7 @@ class HashSet[T >: Null <: AnyRef](powerOfTwoInitialCapacity: Int = 16, loadFact
2743
/** Remove all elements from this set and set back to initial configuration */
2844
def clear(): Unit = {
2945
used = 0
30-
allocate(powerOfTwoInitialCapacity)
46+
allocate(roundToPower(initialCapacity))
3147
}
3248

3349
/** Turn hashcode `x` into a table index */
@@ -36,7 +52,7 @@ class HashSet[T >: Null <: AnyRef](powerOfTwoInitialCapacity: Int = 16, loadFact
3652
/** Hashcode, can be overridden */
3753
def hash(x: T): Int = x.hashCode
3854

39-
private def entryAt(idx: Int) = table.apply(idx).asInstanceOf[T]
55+
private def entryAt(idx: Int) = table(idx).asInstanceOf[T]
4056

4157
/** Find entry such that `isEqual(x, entry)`. If it exists, return it.
4258
* If not, enter `x` in set and return `x`.
@@ -63,7 +79,7 @@ class HashSet[T >: Null <: AnyRef](powerOfTwoInitialCapacity: Int = 16, loadFact
6379
}
6480

6581
/** The entry in the set such that `isEqual(x, entry)`, or else `null`. */
66-
def findEntry(x: T): T = {
82+
def lookup(x: T): T = {
6783
if (Stats.enabled) accesses += 1
6884
var h = index(hash(x))
6985
var entry = entryAt(h)
@@ -77,38 +93,6 @@ class HashSet[T >: Null <: AnyRef](powerOfTwoInitialCapacity: Int = 16, loadFact
7793

7894
private var rover: Int = -1
7995

80-
/** Add entry `x` to set */
81-
def addEntry(x: T): Unit = {
82-
if (Stats.enabled) accesses += 1
83-
var h = index(hash(x))
84-
var entry = entryAt(h)
85-
while (entry ne null) {
86-
if (isEqual(x, entry)) return
87-
if (Stats.enabled) misses += 1
88-
h = index(h + 1)
89-
entry = entryAt(h)
90-
}
91-
table(h) = x
92-
used += 1
93-
if (used > (table.length >> 2)) growTable()
94-
}
95-
96-
/** Add all entries in `xs` to set */
97-
def addEntries(xs: TraversableOnce[T]): Unit =
98-
xs.iterator foreach addEntry
99-
100-
/** The iterator of all elements in the set */
101-
def iterator: Iterator[T] = new Iterator[T] {
102-
private var i = 0
103-
def hasNext: Boolean = {
104-
while (i < table.length && (table(i) eq null)) i += 1
105-
i < table.length
106-
}
107-
def next(): T =
108-
if (hasNext) { i += 1; table(i - 1).asInstanceOf[T] }
109-
else null
110-
}
111-
11296
/** Privileged access: Find first entry with given hashcode */
11397
protected def findEntryByHash(hashCode: Int): T = {
11498
rover = index(hashCode)
@@ -158,5 +142,58 @@ class HashSet[T >: Null <: AnyRef](powerOfTwoInitialCapacity: Int = 16, loadFact
158142
}
159143
}
160144

145+
/** Add entry `x` to set */
146+
def += (x: T): Unit = {
147+
if (Stats.enabled) accesses += 1
148+
var h = index(hash(x))
149+
var entry = entryAt(h)
150+
while (entry ne null) {
151+
if (isEqual(x, entry)) return
152+
if (Stats.enabled) misses += 1
153+
h = index(h + 1)
154+
entry = entryAt(h)
155+
}
156+
table(h) = x
157+
used += 1
158+
if (used > (table.length >> 2)) growTable()
159+
}
160+
161+
def -= (x: T): Unit =
162+
if (Stats.enabled) accesses += 1
163+
var h = index(hash(x))
164+
var entry = entryAt(h)
165+
while entry != null do
166+
if isEqual(x, entry) then
167+
var hole = h
168+
while
169+
h = index(h + 1)
170+
entry = entryAt(h)
171+
entry != null && index(hash(entry)) != h
172+
do
173+
table(hole) = entry
174+
hole = h
175+
table(hole) = null
176+
used -= 1
177+
return
178+
h = index(h + 1)
179+
entry = entryAt(h)
180+
end -=
181+
182+
/** Add all entries in `xs` to set */
183+
def ++= (xs: IterableOnce[T]): Unit =
184+
xs.iterator.foreach(this += _)
185+
186+
/** The iterator of all elements in the set */
187+
def iterator: Iterator[T] = new Iterator[T] {
188+
private var i = 0
189+
def hasNext: Boolean = {
190+
while (i < table.length && (table(i) eq null)) i += 1
191+
i < table.length
192+
}
193+
def next(): T =
194+
if (hasNext) { i += 1; table(i - 1).asInstanceOf[T] }
195+
else null
196+
}
197+
161198
override def toString(): String = "HashSet(%d / %d)".format(used, table.length)
162199
}

compiler/src/dotty/tools/dotc/util/MutableSet.scala

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,21 @@ package dotty.tools.dotc.util
44
*/
55
abstract class MutableSet[T >: Null] {
66

7-
def findEntry(x: T): T
7+
/** The entry in the set such that `isEqual(x, entry)`, or else `null`. */
8+
def lookup(x: T): T /* | Null */
89

9-
def addEntry(x: T): Unit
10+
def +=(x: T): Unit
1011

11-
def iterator: Iterator[T]
12+
def clear(): Unit
1213

13-
def foreach[U](f: T => U): Unit = iterator foreach f
14+
def size: Int
1415

15-
def apply(x: T): Boolean = contains(x)
16+
def iterator: Iterator[T]
1617

17-
def contains(x: T): Boolean =
18-
findEntry(x) != null
18+
def contains(x: T): Boolean = lookup(x) != null
19+
20+
def foreach[U](f: T => U): Unit = iterator foreach f
1921

2022
def toList: List[T] = iterator.toList
2123

22-
def clear(): Unit
2324
}

0 commit comments

Comments
 (0)