From f98fc53cf064033b179c8fe3d194d1a802641a34 Mon Sep 17 00:00:00 2001 From: Frank Mayer Date: Thu, 13 Feb 2025 04:58:27 +0000 Subject: [PATCH] init --- README.md | 9 +++++ go.mod | 3 ++ map.go | 119 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 131 insertions(+) create mode 100644 README.md create mode 100644 go.mod create mode 100644 map.go diff --git a/README.md b/README.md new file mode 100644 index 0000000..5c829bb --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# Speicher + +WORK IN PROGRESS + +Pronunciation: [[ˈʃpaɪ̯çɐ]](https://upload.wikimedia.org/wikipedia/commons/2/21/De-speicher.ogg) + +Speicher is a lightweight, data store. + +![](https://i.imgflip.com/9f9pu3.jpg) diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..8d0d6f5 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module speicher + +go 1.23 diff --git a/map.go b/map.go new file mode 100644 index 0000000..b958296 --- /dev/null +++ b/map.go @@ -0,0 +1,119 @@ +package speicher + +import ( + "encoding/json" + "errors" + "fmt" + "os" + "strings" + "sync" +) + +type ( + MemoryMap[T any] struct { + data map[string]T + location string + mut sync.RWMutex + } + + Map[T any] interface { + Get(key string) (T, bool) + Has(key string) bool + Set(key string, value T) + RangeKV() <-chan MapRangeEl[T] + RangeV() <-chan T + Save() error + Lock() + Unlock() + RLock() + RUnlock() + } + + MapRangeEl[T any] struct { + Key string + Value T + } +) + +func (m *MemoryMap[T]) Get(key string) (value T, found bool) { + value, found = m.data[key] + return +} + +func (m *MemoryMap[T]) Has(key string) bool { + _, ok := m.data[key] + return ok +} + +func (m *MemoryMap[T]) Set(key string, value T) { + m.data[key] = value +} + +func (m *MemoryMap[T]) RangeKV() <-chan MapRangeEl[T] { + ch := make(chan MapRangeEl[T]) + go func() { + defer close(ch) + for key, value := range m.data { + ch <- MapRangeEl[T]{Key: key, Value: value} + } + }() + return ch +} + +func (m *MemoryMap[T]) RangeV() <-chan T { + ch := make(chan T) + go func() { + defer close(ch) + for _, value := range m.data { + ch <- value + } + }() + return ch +} + +func (m *MemoryMap[T]) Lock() { + m.mut.Lock() +} +func (m *MemoryMap[T]) Unlock() { + m.mut.Unlock() + _ = m.Save() +} + +func (m *MemoryMap[T]) RLock() { + m.mut.RLock() +} +func (m *MemoryMap[T]) RUnlock() { + m.mut.RUnlock() +} + +func (m *MemoryMap[T]) Save() error { + f, err := os.OpenFile(m.location, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) + if err != nil { + return errors.Join(fmt.Errorf("failed to open file %s", m.location), err) + } + encoder := json.NewEncoder(f) + if err := encoder.Encode(m.data); err != nil { + return errors.Join(fmt.Errorf("failed to encode json file %s", m.location), err) + } + return nil +} + +func LoadMap[T any](location string) (Map[T], error) { + if strings.HasSuffix(location, ".json") { + return loadMapFromJsonFile[T](location) + } + return nil, fmt.Errorf("unable to find loader for %s", location) +} + +func loadMapFromJsonFile[T any](location string) (Map[T], error) { + f, err := os.OpenFile(location, os.O_RDONLY, 0644) + if err != nil { + return nil, errors.Join(fmt.Errorf("failed to open file %s", location), err) + } + decoder := json.NewDecoder(f) + m := &MemoryMap[T]{} + if err := decoder.Decode(&m.data); err != nil { + return nil, errors.Join(fmt.Errorf("failed to decode json file %s", location), err) + } + return m, nil +}