You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
terraform/internal/collections/map.go

147 lines
4.4 KiB

// Copyright IBM Corp. 2014, 2026
// SPDX-License-Identifier: BUSL-1.1
package collections
import "iter"
// Set represents an associative array from keys of type K to values of type V.
//
// A caller-provided "key function" defines how to produce a comparable unique
// key for each distinct value of type K.
//
// Map operations are not concurrency-safe. Use external locking if multiple
// goroutines might modify the map concurrently or if one goroutine might
// read a map while another is modifying it.
type Map[K, V any] struct {
elems map[UniqueKey[K]]MapElem[K, V]
key func(K) UniqueKey[K]
}
// MapElem represents a single element of a map.
type MapElem[K, V any] struct {
K K
V V
}
// NewMap constructs a new map whose key type knows how to calculate its own
// unique keys, by implementing [UniqueKeyer] of itself.
func NewMap[K UniqueKeyer[K], V any](elems ...MapElem[K, V]) Map[K, V] {
m := NewMapFunc[K, V](K.UniqueKey)
for _, elems := range elems {
m.Put(elems.K, elems.V)
}
return m
}
// NewMapFunc constructs a new map with the given "map function".
//
// A valid key function must produce only values of types that can be compared
// for equality using the Go == operator, and must guarantee that each unique
// value of K has a corresponding key that uniquely identifies it. The
// implementer of the key function can decide what constitutes a
// "unique value of K", based on the meaning of type K.
//
// Type V is unconstrained by the arguments, so callers must explicitly provide
// the key and value type arguments when calling this function.
func NewMapFunc[K, V any](keyFunc func(K) UniqueKey[K]) Map[K, V] {
return Map[K, V]{
elems: make(map[UniqueKey[K]]MapElem[K, V]),
key: keyFunc,
}
}
// NewMapCmp constructs a new set for any comparable key type, using the
// built-in == operator as the definition of key equivalence.
//
// This is here for completeness in case it's useful when talking to a
// generalized API that operates on maps of any key type, but if your
// key type is fixed and known to be comparable then it's pointless to
// use [Map]; use Go's built-in map types instead, which will then avoid
// redundantly storing the keys twice.
func NewMapCmp[K comparable, V any]() Map[K, V] {
return NewMapFunc[K, V](cmpUniqueKeyFunc[K])
}
// HasKey returns true if the map has an element with the given key, or
// false otherwise.
func (m Map[K, V]) HasKey(k K) bool {
if m.key == nil {
return false // an uninitialized map has no keys
}
uniq := m.key(k)
_, exists := m.elems[uniq]
return exists
}
// Get retrieves the value associated with the given key, or the zero value
// of V if no matching element exists in the map.
func (m Map[K, V]) Get(k K) V {
ret, _ := m.GetOk(k)
return ret
}
// GetOk is like [Map.Get] but also returns a second boolean result reporting
// whether a matching element was present in the map.
func (m Map[K, V]) GetOk(k K) (V, bool) {
if m.key == nil {
var zero V
return zero, false // an uninitialized map has no keys
}
uniq := m.key(k)
ret, ok := m.elems[uniq]
return ret.V, ok
}
// Put writes a new element into the map, with the given key and value.
//
// If there is already an element with an equivalent key (as determined by the
// set's "key function") then the new element replaces that existing element.
func (m Map[K, V]) Put(k K, v V) {
if m.key == nil {
panic("Put into uninitialized collections.Map")
}
uniq := m.key(k)
m.elems[uniq] = MapElem[K, V]{
K: k,
V: v,
}
}
// Delete removes from the map the element with the given key, or does nothing
// if there is no such element.
func (m Map[K, V]) Delete(k K) {
if m.key == nil {
panic("Delete from uninitialized collections.Map")
}
uniq := m.key(k)
delete(m.elems, uniq)
}
// All returns an iterator over the elements of the map, in an unspecified
// order.
//
// This is intended for use in a range-over-func statement, like this:
//
// for k, v := range map.All() {
// // do something with k and/or v
// }
//
// Modifying the map during iteration causes unspecified results. Modifying
// the map concurrently with advancing the iterator causes undefined behavior
// including possible memory unsafety.
func (m Map[K, V]) All() iter.Seq2[K, V] {
return func(yield func(K, V) bool) {
for _, elem := range m.elems {
if !yield(elem.K, elem.V) {
return
}
}
}
}
// Len returns the number of elements in the map.
func (m Map[K, V]) Len() int {
return len(m.elems)
}