diff options
| author | 2026-02-14 00:55:12 +0300 | |
|---|---|---|
| committer | 2026-02-14 01:07:49 +0300 | |
| commit | adaaa6bd68a4af7c2e9a7f4c2ed2036a369d707d (patch) | |
| tree | 0f8c85af9456f1edd3b96a75a36f9b96e4ebecc3 /container | |
| download | std-master.tar.gz std-master.tar.bz2 std-master.tar.xz std-master.zip | |
Diffstat (limited to '')
| -rw-r--r-- | container/smap.go | 104 | ||||
| -rw-r--r-- | container/stack.go | 28 | ||||
| -rw-r--r-- | container/stack_test.go | 350 |
3 files changed, 482 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 + } + } +} diff --git a/container/stack.go b/container/stack.go new file mode 100644 index 0000000..0f5a640 --- /dev/null +++ b/container/stack.go @@ -0,0 +1,28 @@ +// Package container содержит стандартные контейнеры для данных. +package container + +// Stack container. +type Stack[T any] []T + +// Push element to stack. +func (s *Stack[T]) Push(e T) { + *s = append(*s, e) +} + +// Pop element from stack. +func (s *Stack[T]) Pop() T { + var e T + if len(*s) > 0 { + e, *s = (*s)[len(*s)-1], (*s)[:len(*s)-1] + } + return e +} + +// Top element on stack. +func (s *Stack[T]) Top() T { + var e T + if len(*s) > 0 { + e = (*s)[len(*s)-1] + } + return e +} diff --git a/container/stack_test.go b/container/stack_test.go new file mode 100644 index 0000000..711e6bb --- /dev/null +++ b/container/stack_test.go @@ -0,0 +1,350 @@ +package container_test + +import ( + "fmt" + "reflect" + "testing" + + "go.neonxp.ru/pkg/container" +) + +func TestGet(t *testing.T) { + m := container.Make[string, int](0) + m.Set("one", 1) + m.Set("two", 2) + + if val := m.Get("one"); val != 1 { + t.Errorf("expected Get(\"one\") = 1, got %d", val) + } + + if val := m.Get("two"); val != 2 { + t.Errorf("expected Get(\"two\") = 2, got %d", val) + } + + if val := m.Get("three"); val != 0 { + t.Errorf("expected Get(\"three\") = 0 (zero value), got %d", val) + } +} + +func TestGet2(t *testing.T) { + m := container.Make[string, int](0) + m.Set("one", 1) + m.Set("two", 2) + + // Проверяем существующий ключ + if val, ok := m.Get2("one"); !ok || val != 1 { + t.Errorf("expected Get2(\"one\") = (1, true), got (%d, %v)", val, ok) + } + + // Проверяем другой существующий ключ + if val, ok := m.Get2("two"); !ok || val != 2 { + t.Errorf("expected Get2(\"two\") = (2, true), got (%d, %v)", val, ok) + } + + // Проверяем несуществующий ключ + if val, ok := m.Get2("three"); ok || val != 0 { + t.Errorf("expected Get2(\"three\") = (0, false), got (%d, %v)", val, ok) + } + + // Проверяем пустую карту + m2 := container.Make[string, int](0) + if val, ok := m2.Get2("one"); ok || val != 0 { + t.Errorf("expected Get2(\"one\") = (0, false) for empty map, got (%d, %v)", val, ok) + } + + // Проверяем после удаления + m.Delete("one") + if val, ok := m.Get2("one"); ok || val != 0 { + t.Errorf("expected Get2(\"one\") = (0, false) after deletion, got (%d, %v)", val, ok) + } +} + +func TestSet(t *testing.T) { + m := container.Make[string, int](0) + + m.Set("one", 1) + if val := m.Get("one"); val != 1 { + t.Errorf("expected value 1 after Set, got %d", val) + } + + m.Set("one", 2) + if val := m.Get("one"); val != 2 { + t.Errorf("expected value 2 after second Set, got %d", val) + } + + keysCount := make(map[string]int) + m.Keys(func(k string) bool { + keysCount[k]++ + return true + }) + + if keysCount["one"] != 1 { + t.Errorf("expected key \"one\" to appear once in keys, got %d times", keysCount["one"]) + } + + var keys []string + m.Keys(func(k string) bool { + keys = append(keys, k) + return true + }) + + if len(keys) != 1 { + t.Errorf("expected keys length 1, got %d", len(keys)) + } + + if keys[0] != "one" { + t.Errorf("expected first key to be \"one\", got %s", keys[0]) + } +} + +func TestDelete(t *testing.T) { + m := container.Make[string, int](0) + m.Set("one", 1) + m.Set("two", 2) + m.Set("three", 3) + + // Проверяем, что ключ существует перед удалением + if !m.Has("one") { + t.Error("expected key \"one\" to exist before deletion") + } + + // Удаляем ключ + m.Delete("one") + + // Проверяем, что ключ больше не существует + if m.Has("one") { + t.Error("expected key \"one\" to be deleted") + } + + // Проверяем, что значение по ключу - нулевое + if val := m.Get("one"); val != 0 { + t.Errorf("expected Get(\"one\") = 0 after deletion, got %d", val) + } + + // Проверяем, что другие ключи остались + if !m.Has("two") { + t.Error("expected key \"two\" to still exist") + } + if !m.Has("three") { + t.Error("expected key \"three\" to still exist") + } + + // Проверяем порядок ключей + var keys []string + m.Keys(func(k string) bool { + keys = append(keys, k) + return true + }) + + expected := []string{"two", "three"} + if !reflect.DeepEqual(keys, expected) { + t.Errorf("expected keys %v after deletion, got %v", expected, keys) + } + + // Проверяем удаление несуществующего ключа + m.Delete("nonexistent") + // Должно пройти без паники +} + +func TestHas(t *testing.T) { + m := container.Make[string, int](0) + + // Проверяем отсутствие ключа в пустой карте + if m.Has("one") { + t.Error("expected Has(\"one\") = false for empty map") + } + + // Добавляем ключ + m.Set("one", 1) + if !m.Has("one") { + t.Error("expected Has(\"one\") = true after Set") + } + + // Проверяем другой ключ + if m.Has("two") { + t.Error("expected Has(\"two\") = false for non-existent key") + } + + // Удаляем ключ + m.Delete("one") + if m.Has("one") { + t.Error("expected Has(\"one\") = false after Delete") + } +} + +func TestKeys(t *testing.T) { + m := container.Make[string, int](0) + m.Set("one", 1) + m.Set("two", 2) + m.Set("three", 3) + + var keys []string + m.Keys(func(k string) bool { + keys = append(keys, k) + return true + }) + + expected := []string{"one", "two", "three"} + if !reflect.DeepEqual(keys, expected) { + t.Errorf("expected keys %v, got %v", expected, keys) + } + + // Test early termination + var partialKeys []string + m.Keys(func(k string) bool { + partialKeys = append(partialKeys, k) + return len(partialKeys) < 2 // stop after 2 keys + }) + + if len(partialKeys) != 2 { + t.Errorf("expected 2 keys with early termination, got %d", len(partialKeys)) + } + + if partialKeys[0] != "one" || partialKeys[1] != "two" { + t.Errorf("expected first two keys to be \"one\", \"two\", got %v", partialKeys) + } +} + +func TestValues(t *testing.T) { + m := container.Make[string, int](0) + m.Set("one", 1) + m.Set("two", 2) + m.Set("three", 3) + + var values []int + m.Values(func(v int) bool { + values = append(values, v) + return true + }) + + expected := []int{1, 2, 3} + if !reflect.DeepEqual(values, expected) { + t.Errorf("expected values %v, got %v", expected, values) + } + + // Test early termination + var partialValues []int + m.Values(func(v int) bool { + partialValues = append(partialValues, v) + return len(partialValues) < 2 // stop after 2 values + }) + + if len(partialValues) != 2 { + t.Errorf("expected 2 values with early termination, got %d", len(partialValues)) + } +} + +func TestEntries(t *testing.T) { + m := container.Make[string, int](0) + m.Set("one", 1) + m.Set("two", 2) + m.Set("three", 3) + + var entries []struct { + k string + v int + } + m.Entries(func(k string, v int) bool { + entries = append(entries, struct { + k string + v int + }{k, v}) + return true + }) + + expected := []struct { + k string + v int + }{ + {"one", 1}, + {"two", 2}, + {"three", 3}, + } + if !reflect.DeepEqual(entries, expected) { + t.Errorf("expected entries %v, got %v", expected, entries) + } + + // Test early termination + var partialEntries []struct { + k string + v int + } + m.Entries(func(k string, v int) bool { + partialEntries = append(partialEntries, struct { + k string + v int + }{k, v}) + return len(partialEntries) < 2 // stop after 2 entries + }) + + if len(partialEntries) != 2 { + t.Errorf("expected 2 entries with early termination, got %d", len(partialEntries)) + } +} + +func ExampleMap() { + // New map with capacity = 10 + m := container.Make[string, int](10) + + // Set example values + m.Set("one", 1) + m.Set("two", 2) + m.Set("three", 3) + + // Get first value + fmt.Println(`m.Get("one") =`, m.Get("one")) + + // Get value with existence check + if val, ok := m.Get2("one"); ok { + fmt.Println(`m.Get2("one") =`, val) + } else { + fmt.Println(`m.Get2("one") = key not found`) + } + + // Get value with existence check + if val, ok := m.Get2("four"); ok { + fmt.Println(`m.Get2("four") =`, val) + } else { + fmt.Println(`m.Get2("four") = key not found`) + } + + // Keys iteration + for k := range m.Keys { + fmt.Println("key iteration:", k) + } + + // Values iteration + for v := range m.Values { + fmt.Println("value iteration:", v) + } + + // Key:Value pairs iteration + for k, v := range m.Entries { + fmt.Println("pairs iteration:", k, v) + } + + // Check if key exists + fmt.Println("Has 'two':", m.Has("two")) + + // Delete a key + m.Delete("two") + + // Check if key exists + fmt.Println("Has 'two':", m.Has("two")) + + // Output: + // m.Get("one") = 1 + // m.Get2("one") = 1 + // m.Get2("four") = key not found + // key iteration: one + // key iteration: two + // key iteration: three + // value iteration: 1 + // value iteration: 2 + // value iteration: 3 + // pairs iteration: one 1 + // pairs iteration: two 2 + // pairs iteration: three 3 + // Has 'two': true + // Has 'two': false +} |
