@@ -10,7 +10,7 @@ import StdNames.str
1010import scala .internal .Chars .isIdentifierStart
1111import collection .immutable
1212import config .Config
13- import util .LinearMap
13+ import util .{ LinearMap , HashSet }
1414
1515import scala .annotation .internal .sharable
1616
@@ -262,10 +262,9 @@ object Names {
262262 }
263263
264264 /** 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 {
267266
268- /** The n'th character */
267+ /** The n'th character */
269268 def apply (n : Int ): Char = chrs(start + n)
270269
271270 /** A character in this name satisfies predicate `p` */
@@ -499,27 +498,70 @@ object Names {
499498 override def debugString : String = s " ${underlying.debugString}[ $info] "
500499 }
501500
501+ /** The term name represented by the empty string */
502+ val EmptyTermName : SimpleName = SimpleName (- 1 , 0 )
503+
502504 // Nametable
503505
504- private final val InitialHashSize = 0x8000
505- private final val InitialNameSize = 0x20000
506- private final val fillFactor = 0.7
506+ inline val InitialNameSize = 0x20000
507507
508508 /** 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
510510 private [dotty] var chrs : Array [Char ] = new Array [Char ](InitialNameSize )
511511
512512 /** 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
514514 private var nc = 0
515515
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
519561
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 ()
523565
524566 /** The hash of a name made of from characters cs[offset..offset+len-1]. */
525567 private def hashValue (cs : Array [Char ], offset : Int , len : Int ): Int = {
@@ -545,62 +587,8 @@ object Names {
545587 /** Create a term name from the characters in cs[offset..offset+len-1].
546588 * Assume they are already encoded.
547589 */
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)
604592
605593 /** Create a type name from the characters in cs[offset..offset+len-1].
606594 * Assume they are already encoded.
@@ -631,11 +619,6 @@ object Names {
631619 /** Create a type name from a string */
632620 def typeName (s : String ): TypeName = typeName(s.toCharArray, 0 , s.length)
633621
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-
639622 /** The type name represented by the empty string */
640623 val EmptyTypeName : TypeName = EmptyTermName .toTypeName
641624
0 commit comments