aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author2026-02-14 00:55:12 +0300
committer2026-02-14 01:07:49 +0300
commitadaaa6bd68a4af7c2e9a7f4c2ed2036a369d707d (patch)
tree0f8c85af9456f1edd3b96a75a36f9b96e4ebecc3
downloadstd-master.tar.gz
std-master.tar.bz2
std-master.tar.xz
std-master.zip
added stack, added smapHEADmaster
Diffstat (limited to '')
-rw-r--r--README.md22
-rw-r--r--container/smap.go104
-rw-r--r--container/stack.go28
-rw-r--r--container/stack_test.go350
-rw-r--r--go.mod3
5 files changed, 507 insertions, 0 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0f0c424
--- /dev/null
+++ b/README.md
@@ -0,0 +1,22 @@
+# Пакет pkg
+
+Пакет для утилитарных функций, которые я не хочу каждый раз заново повторять.
+
+## Лицензия
+
+
+Этот проект лицензирован в соответствии с GNU General Public License версии 3
+(GPLv3). Подробности смотрите в файле [LICENSE](LICENSE).
+
+```
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2026 Alexander NeonXP Kiryukhin <i@neonxp.ru>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+```
+
+## Автор
+
+- Александр Кирюхин <i@neonxp.ru>
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
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..b7db628
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,3 @@
+module go.neonxp.ru/std
+
+go 1.25.0