| // Copyright 2024 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package unique |
| |
| import ( |
| "internal/abi" |
| isync "internal/sync" |
| "unsafe" |
| ) |
| |
| var zero uintptr |
| |
| // Handle is a globally unique identity for some value of type T. |
| // |
| // Two handles compare equal exactly if the two values used to create the handles |
| // would have also compared equal. The comparison of two handles is trivial and |
| // typically much more efficient than comparing the values used to create them. |
| type Handle[T comparable] struct { |
| value *T |
| } |
| |
| // Value returns a shallow copy of the T value that produced the Handle. |
| // Value is safe for concurrent use by multiple goroutines. |
| func (h Handle[T]) Value() T { |
| return *h.value |
| } |
| |
| // Make returns a globally unique handle for a value of type T. Handles |
| // are equal if and only if the values used to produce them are equal. |
| // Make is safe for concurrent use by multiple goroutines. |
| func Make[T comparable](value T) Handle[T] { |
| // Find the map for type T. |
| typ := abi.TypeFor[T]() |
| if typ.Size() == 0 { |
| return Handle[T]{(*T)(unsafe.Pointer(&zero))} |
| } |
| ma, ok := uniqueMaps.Load(typ) |
| if !ok { |
| m := &uniqueMap[T]{canonMap: newCanonMap[T](), cloneSeq: makeCloneSeq(typ)} |
| ma, _ = uniqueMaps.LoadOrStore(typ, m) |
| } |
| m := ma.(*uniqueMap[T]) |
| |
| // Find the value in the map. |
| ptr := m.Load(value) |
| if ptr == nil { |
| // Insert a new value into the map. |
| ptr = m.LoadOrStore(clone(value, &m.cloneSeq)) |
| } |
| return Handle[T]{ptr} |
| } |
| |
| // uniqueMaps is an index of type-specific concurrent maps used for unique.Make. |
| // |
| // The two-level map might seem odd at first since the HashTrieMap could have "any" |
| // as its key type, but the issue is escape analysis. We do not want to force lookups |
| // to escape the argument, and using a type-specific map allows us to avoid that where |
| // possible (for example, for strings and plain-ol'-data structs). We also get the |
| // benefit of not cramming every different type into a single map, but that's certainly |
| // not enough to outweigh the cost of two map lookups. What is worth it though, is saving |
| // on those allocations. |
| var uniqueMaps isync.HashTrieMap[*abi.Type, any] // any is always a *uniqueMap[T]. |
| |
| type uniqueMap[T comparable] struct { |
| *canonMap[T] |
| cloneSeq |
| } |