Created
July 4, 2024 13:10
-
-
Save KevinWang15/5df5ff58a04ae330694abaa258aaf623 to your computer and use it in GitHub Desktop.
go - attaching arbitrary data to any pointer type
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package attachable | |
import ( | |
"fmt" | |
"strings" | |
"sync" | |
) | |
// valuesAttached is a concurrent map to store values associated with pointer instances | |
var valuesAttached = sync.Map{} | |
// AttachValue attaches a value to a specific pointer instance | |
func AttachValue[T any](ptr *T, key string, value any) { | |
// Create a unique key by combining the pointer address and the provided key | |
ptrKey := getPtrKey(ptr, key) | |
// Store the value in the concurrent map | |
valuesAttached.Store(ptrKey, value) | |
} | |
// ReadValue reads a value attached to a specific pointer instance | |
func ReadValue[T any](ptr *T, key string) (any, bool) { | |
// Create a unique key by combining the pointer address and the provided key | |
ptrKey := getPtrKey(ptr, key) | |
// Retrieve the value from the concurrent map | |
return valuesAttached.Load(ptrKey) | |
} | |
// ClearAllAttachedValues removes all values associated with a specific pointer instance | |
func ClearAllAttachedValues[T any](ptr *T) { | |
// Iterate through all keys in the concurrent map | |
valuesAttached.Range(func(key, value interface{}) bool { | |
// Check if the key belongs to the given pointer | |
if ptrKeyString, ok := key.(string); ok && strings.HasPrefix(ptrKeyString, getPtrPrefix(ptr)) { | |
// Remove the key-value pair | |
valuesAttached.Delete(key) | |
} | |
return true | |
}) | |
} | |
// getPtrKey generates a unique key for a pointer instance and a given key | |
func getPtrKey[T any](ptr *T, key string) string { | |
return fmt.Sprintf("%p:%s", ptr, key) | |
} | |
// getPtrPrefix returns the prefix used for keys associated with a specific pointer | |
func getPtrPrefix[T any](ptr *T) string { | |
return fmt.Sprintf("%p:", ptr) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package attachable | |
import ( | |
"testing" | |
) | |
type testStruct struct { | |
value int | |
} | |
func TestAttachAndReadValue(t *testing.T) { | |
ptr1 := &testStruct{value: 1} | |
ptr2 := &testStruct{value: 2} | |
// Test attaching and reading values | |
AttachValue(ptr1, "key1", "value1") | |
AttachValue(ptr1, "key2", 42) | |
AttachValue(ptr2, "key1", "value2") | |
tests := []struct { | |
name string | |
ptr *testStruct | |
key string | |
expected interface{} | |
exists bool | |
}{ | |
{"ptr1 key1", ptr1, "key1", "value1", true}, | |
{"ptr1 key2", ptr1, "key2", 42, true}, | |
{"ptr2 key1", ptr2, "key1", "value2", true}, | |
{"ptr1 non-existent key", ptr1, "key3", nil, false}, | |
{"ptr2 non-existent key", ptr2, "key2", nil, false}, | |
} | |
for _, tt := range tests { | |
t.Run(tt.name, func(t *testing.T) { | |
value, exists := ReadValue(tt.ptr, tt.key) | |
if exists != tt.exists { | |
t.Errorf("ReadValue() exists = %v, want %v", exists, tt.exists) | |
} | |
if exists && value != tt.expected { | |
t.Errorf("ReadValue() value = %v, want %v", value, tt.expected) | |
} | |
}) | |
} | |
} | |
func TestClearAllAttachedValues(t *testing.T) { | |
ptr := &testStruct{value: 1} | |
// Attach multiple values | |
AttachValue(ptr, "key1", "value1") | |
AttachValue(ptr, "key2", 42) | |
AttachValue(ptr, "key3", true) | |
// Clear all attached values | |
ClearAllAttachedValues(ptr) | |
// Check if all values are cleared | |
tests := []struct { | |
name string | |
key string | |
}{ | |
{"key1", "key1"}, | |
{"key2", "key2"}, | |
{"key3", "key3"}, | |
} | |
for _, tt := range tests { | |
t.Run(tt.name, func(t *testing.T) { | |
_, exists := ReadValue(ptr, tt.key) | |
if exists { | |
t.Errorf("Value for key %s should not exist after clearing", tt.key) | |
} | |
}) | |
} | |
} | |
func TestMultiplePointers(t *testing.T) { | |
ptr1 := &testStruct{value: 1} | |
ptr2 := &testStruct{value: 2} | |
// Attach values to both pointers | |
AttachValue(ptr1, "key", "value1") | |
AttachValue(ptr2, "key", "value2") | |
// Read and verify values | |
value1, exists1 := ReadValue(ptr1, "key") | |
value2, exists2 := ReadValue(ptr2, "key") | |
if !exists1 || value1 != "value1" { | |
t.Errorf("Expected value1 to be 'value1', got %v", value1) | |
} | |
if !exists2 || value2 != "value2" { | |
t.Errorf("Expected value2 to be 'value2', got %v", value2) | |
} | |
// Clear values for ptr1 | |
ClearAllAttachedValues(ptr1) | |
// Verify ptr1 values are cleared but ptr2 values remain | |
_, exists1 = ReadValue(ptr1, "key") | |
value2, exists2 = ReadValue(ptr2, "key") | |
if exists1 { | |
t.Error("Value for ptr1 should not exist after clearing") | |
} | |
if !exists2 || value2 != "value2" { | |
t.Errorf("Expected value2 to still be 'value2', got %v", value2) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment