| // Copyright 2025 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 fips140cache provides a weak map that associates the lifetime of |
| // values with the lifetime of keys. |
| // |
| // It can be used to associate a precomputed value (such as an internal/fips140 |
| // PrivateKey value, which in FIPS 140-3 mode may have required an expensive |
| // pairwise consistency test) with a type that doesn't have private fields (such |
| // as an ed25519.PrivateKey), or that can't be safely modified because it may be |
| // concurrently copied (such as an ecdsa.PrivateKey). |
| package fips140cache |
| |
| import ( |
| "runtime" |
| "sync" |
| "weak" |
| ) |
| |
| type Cache[K, V any] struct { |
| m sync.Map |
| } |
| |
| // Get returns the result of new, for an associated key k. |
| // |
| // If Get was called with k before and didn't return an error, Get may return |
| // the same value it returned from the previous call if check returns true on |
| // it. If check returns false, Get will call new again and return the result. |
| // |
| // The cache is evicted some time after k becomes unreachable. |
| func (c *Cache[K, V]) Get(k *K, new func() (*V, error), check func(*V) bool) (*V, error) { |
| p := weak.Make(k) |
| if cached, ok := c.m.Load(p); ok { |
| v := cached.(*V) |
| if check(v) { |
| return v, nil |
| } |
| } |
| v, err := new() |
| if err != nil { |
| return nil, err |
| } |
| if _, present := c.m.Swap(p, v); !present { |
| runtime.AddCleanup(k, c.evict, p) |
| } |
| return v, nil |
| } |
| |
| func (c *Cache[K, V]) evict(p weak.Pointer[K]) { |
| c.m.Delete(p) |
| } |