@@ -10,7 +10,7 @@ import StdNames.str
10
10
import scala .internal .Chars .isIdentifierStart
11
11
import collection .immutable
12
12
import config .Config
13
- import util .LinearMap
13
+ import util .{ LinearMap , HashSet }
14
14
15
15
import scala .annotation .internal .sharable
16
16
@@ -262,10 +262,9 @@ object Names {
262
262
}
263
263
264
264
/** A simple name is essentially an interned string */
265
- final class SimpleName (val start : Int , val length : Int , @ sharable private [Names ] var next : SimpleName ) extends TermName {
266
- // `next` is @sharable because it is only modified in the synchronized block of termName.
265
+ final class SimpleName (val start : Int , val length : Int ) extends TermName {
267
266
268
- /** The n'th character */
267
+ /** The n'th character */
269
268
def apply (n : Int ): Char = chrs(start + n)
270
269
271
270
/** A character in this name satisfies predicate `p` */
@@ -499,27 +498,70 @@ object Names {
499
498
override def debugString : String = s " ${underlying.debugString}[ $info] "
500
499
}
501
500
501
+ /** The term name represented by the empty string */
502
+ val EmptyTermName : SimpleName = SimpleName (- 1 , 0 )
503
+
502
504
// Nametable
503
505
504
- private final val InitialHashSize = 0x8000
505
- private final val InitialNameSize = 0x20000
506
- private final val fillFactor = 0.7
506
+ inline val InitialNameSize = 0x20000
507
507
508
508
/** Memory to store all names sequentially. */
509
- @ sharable // because it's only mutated in synchronized block of termName
509
+ @ sharable // because it's only mutated in synchronized block of enterIfNew
510
510
private [dotty] var chrs : Array [Char ] = new Array [Char ](InitialNameSize )
511
511
512
512
/** The number of characters filled. */
513
- @ sharable // because it's only mutated in synchronized block of termName
513
+ @ sharable // because it's only mutated in synchronized block of enterIfNew
514
514
private var nc = 0
515
515
516
- /** Hashtable for finding term names quickly. */
517
- @ sharable // because it's only mutated in synchronized block of termName
518
- private var table = new Array [SimpleName ](InitialHashSize )
516
+ /** Make sure the capacity of the character array is at least `n` */
517
+ private def ensureCapacity (n : Int ) =
518
+ if n > chrs.length then
519
+ val newchrs = new Array [Char ](chrs.length * 2 )
520
+ chrs.copyToArray(newchrs)
521
+ chrs = newchrs
522
+
523
+ private class NameTable extends HashSet [SimpleName ](initialCapacity = 0x10000 , capacityMultiple = 2 ):
524
+ import util .Stats
525
+
526
+ override def hash (x : SimpleName ) = hashValue(chrs, x.start, x.length) // needed for resize
527
+ override def isEqual (x : SimpleName , y : SimpleName ) = ??? // not needed
528
+
529
+ def enterIfNew (cs : Array [Char ], offset : Int , len : Int ): SimpleName =
530
+ Stats .record(statsItem(" put" ))
531
+ val table = currentTable
532
+ var idx = hashValue(cs, offset, len) & (table.length - 1 )
533
+ var name = table(idx).asInstanceOf [SimpleName ]
534
+ while name != null do
535
+ if name.length == len && Names .equals(name.start, cs, offset, len) then
536
+ return name
537
+ Stats .record(statsItem(" miss" ))
538
+ idx = (idx + 1 ) & (table.length - 1 )
539
+ name = table(idx).asInstanceOf [SimpleName ]
540
+ Stats .record(statsItem(" addEntryAt" ))
541
+ synchronized {
542
+ if (table eq currentTable) && table(idx) == null then
543
+ // Our previous unsynchronized computation of the next free index is still correct.
544
+ // This relies on the fact that table entries go from null to non-null, and then
545
+ // stay the same. Note that we do not need the table or the entry in it to be
546
+ // volatile since SimpleNames are immutable, and hence safely published.
547
+ // The same holds for the chrs array. We might miss before the synchronized
548
+ // on published characters but that would make name comparison false, which
549
+ // means we end up in the synchronized block here, where we get the correct state
550
+ name = SimpleName (nc, len)
551
+ ensureCapacity(nc + len)
552
+ Array .copy(cs, offset, chrs, nc, len)
553
+ nc += len
554
+ addEntryAt(idx, name)
555
+ else
556
+ enterIfNew(cs, offset, len)
557
+ }
558
+
559
+ addEntryAt(0 , EmptyTermName )
560
+ end NameTable
519
561
520
- /** The number of defined names. */
521
- @ sharable // because it's only mutated in synchronized block of termName
522
- private var size = 1
562
+ /** Hashtable for finding term names quickly . */
563
+ @ sharable // because it's only mutated in synchronized block of enterIfNew
564
+ private val nameTable = NameTable ()
523
565
524
566
/** The hash of a name made of from characters cs[offset..offset+len-1]. */
525
567
private def hashValue (cs : Array [Char ], offset : Int , len : Int ): Int = {
@@ -545,62 +587,8 @@ object Names {
545
587
/** Create a term name from the characters in cs[offset..offset+len-1].
546
588
* Assume they are already encoded.
547
589
*/
548
- def termName (cs : Array [Char ], offset : Int , len : Int ): SimpleName = synchronized {
549
- util.Stats .record(" termName" )
550
- val h = hashValue(cs, offset, len) & (table.length - 1 )
551
-
552
- /** Make sure the capacity of the character array is at least `n` */
553
- def ensureCapacity (n : Int ) =
554
- if (n > chrs.length) {
555
- val newchrs = new Array [Char ](chrs.length * 2 )
556
- chrs.copyToArray(newchrs)
557
- chrs = newchrs
558
- }
559
-
560
- /** Enter characters into chrs array. */
561
- def enterChars (): Unit = {
562
- ensureCapacity(nc + len)
563
- var i = 0
564
- while (i < len) {
565
- chrs(nc + i) = cs(offset + i)
566
- i += 1
567
- }
568
- nc += len
569
- }
570
-
571
- /** Rehash chain of names */
572
- def rehash (name : SimpleName ): Unit =
573
- if (name != null ) {
574
- val oldNext = name.next
575
- val h = hashValue(chrs, name.start, name.length) & (table.size - 1 )
576
- name.next = table(h)
577
- table(h) = name
578
- rehash(oldNext)
579
- }
580
-
581
- /** Make sure the hash table is large enough for the given load factor */
582
- def incTableSize () = {
583
- size += 1
584
- if (size.toDouble / table.size > fillFactor) {
585
- val oldTable = table
586
- table = new Array [SimpleName ](table.size * 2 )
587
- for (i <- 0 until oldTable.size) rehash(oldTable(i))
588
- }
589
- }
590
-
591
- val next = table(h)
592
- var name = next
593
- while (name ne null ) {
594
- if (name.length == len && equals(name.start, cs, offset, len))
595
- return name
596
- name = name.next
597
- }
598
- name = new SimpleName (nc, len, next)
599
- enterChars()
600
- table(h) = name
601
- incTableSize()
602
- name
603
- }
590
+ def termName (cs : Array [Char ], offset : Int , len : Int ): SimpleName =
591
+ nameTable.enterIfNew(cs, offset, len)
604
592
605
593
/** Create a type name from the characters in cs[offset..offset+len-1].
606
594
* Assume they are already encoded.
@@ -631,11 +619,6 @@ object Names {
631
619
/** Create a type name from a string */
632
620
def typeName (s : String ): TypeName = typeName(s.toCharArray, 0 , s.length)
633
621
634
- table(0 ) = new SimpleName (- 1 , 0 , null )
635
-
636
- /** The term name represented by the empty string */
637
- val EmptyTermName : TermName = table(0 )
638
-
639
622
/** The type name represented by the empty string */
640
623
val EmptyTypeName : TypeName = EmptyTermName .toTypeName
641
624
0 commit comments