aboutsummaryrefslogtreecommitdiff
path: root/container/smap.go
diff options
context:
space:
mode:
Diffstat (limited to 'container/smap.go')
-rw-r--r--container/smap.go104
1 files changed, 104 insertions, 0 deletions
diff --git a/container/smap.go b/container/smap.go
new file mode 100644
index 0000000..688ec40
--- /dev/null
+++ b/container/smap.go
@@ -0,0 +1,104 @@
+package container
+
+import (
+ "slices"
+ "sync"
+)
+
+// M is ordered and concurent safe map implementation.
+type M[K comparable, V any] struct {
+ m map[K]V
+ k []K
+ mu sync.RWMutex
+}
+
+// Make returns new instance with `capacity` initial capacity.
+func Make[K comparable, V any](capacity int) M[K, V] {
+ return M[K, V]{
+ m: make(map[K]V, capacity),
+ k: make([]K, 0, capacity),
+ }
+}
+
+// Get value `V` by key `k K`. Returns zero value if the key is absent.
+func (m *M[K, V]) Get(k K) V {
+ m.mu.RLock()
+ defer m.mu.RUnlock()
+
+ return m.m[k]
+}
+
+// Get2 value `v V` by key `k K` and a presence flag `ok bool`. Returns
+// `(value, true)` if the key exists, and `(zero value, false)` if the key is
+// absent.
+func (m *M[K, V]) Get2(k K) (v V, ok bool) {
+ m.mu.RLock()
+ defer m.mu.RUnlock()
+ v, ok = m.m[k]
+
+ return v, ok
+}
+
+// Has returns true if key `k K` exists in map.
+func (m *M[K, V]) Has(k K) bool {
+ m.mu.RLock()
+ defer m.mu.RUnlock()
+
+ _, ok := m.m[k]
+
+ return ok
+}
+
+// Set value `v V` to key `k K`.
+func (m *M[K, V]) Set(k K, v V) {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+
+ if _, exists := m.m[k]; !exists {
+ m.k = append(m.k, k)
+ }
+
+ m.m[k] = v
+}
+
+// Delete key `k K` from map.
+func (m *M[K, V]) Delete(k K) {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+
+ if _, ok := m.m[k]; !ok {
+ return
+ }
+
+ idx := slices.Index(m.k, k)
+ m.k = slices.Delete(m.k, idx, idx+1)
+
+ delete(m.m, k)
+}
+
+// Keys is iterator over keys.
+func (m *M[K, V]) Keys(yield func(K) bool) {
+ for _, k := range m.k {
+ if !yield(k) {
+ break
+ }
+ }
+}
+
+// Values is iterator over values.
+func (m *M[K, V]) Values(yield func(V) bool) {
+ for _, k := range m.k {
+ if !yield(m.m[k]) {
+ break
+ }
+ }
+}
+
+// Entries is iterator over key:value pairs.
+func (m *M[K, V]) Entries(yield func(K, V) bool) {
+ for _, k := range m.k {
+ if !yield(k, m.m[k]) {
+ break
+ }
+ }
+}